• 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.v33
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.Utils
36 import com.android.permissioncontroller.permission.utils.navigateSafe
37 import com.android.settingslib.RestrictedLockUtils
38 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
39 import java.util.stream.Collectors
40 
41 /**
42  * View model for legacy {@link ReviewPermissionsFragment}.
43  */
44 class ReviewPermissionsViewModel(
45     val app: Application,
46     val packageInfo: PackageInfo
47 ) : ViewModel() {
48 
49     private val mUser = android.os.Process.myUserHandle()
50 
51     /**
52      * Holds permission groups for a package or an empty map in case no user review is required.
53      */
54     val permissionGroupsLiveData =
55         object : SmartUpdateMediatorLiveData<Map<String, LightAppPermGroup>>() {
56             val packagePermsLiveData = PackagePermissionsLiveData[packageInfo.packageName, mUser]
57 
58             init {
59                 addSource(packagePermsLiveData) {
60                     update()
61                 }
62             }
63 
64             val permissionGroups = mutableMapOf<String, LightAppPermGroupLiveData>()
65 
66             override fun onUpdate() {
67                 val permissionGroupsMap = packagePermsLiveData.value ?: return
68                 val filteredGroups = permissionGroupsMap.keys.stream()
69                     .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) }
70                     .collect(Collectors.toList())
71 
72                 val getPermGroupLiveData = { permGroupName: String ->
73                     LightAppPermGroupLiveData[packageInfo.packageName, permGroupName, mUser]
74                 }
75                 setSourcesToDifference(filteredGroups, permissionGroups, getPermGroupLiveData)
76                 if (permissionGroups.values.all { it.isInitialized } &&
77                     permissionGroups.values.all { !it.isStale }) {
78                     val permGroups: List<LightAppPermGroup?> = permissionGroups.values.map {
79                         it.value }
80                     val reviewGroups = permGroups.filterNotNull().filter {
81                         shouldShowPermission(it) &&
82                             Utils.OS_PKG == it.permGroupInfo.packageName
83                     }.associateBy {
84                         it.permGroupName
85                     }
86                     value = if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups
87                     else emptyMap()
88                 }
89             }
90         }
91 
92     fun isInitialized(): Boolean {
93         return permissionGroupsLiveData.isInitialized
94     }
95 
96     private fun shouldShowPermission(group: LightAppPermGroup): Boolean {
97         if (!(group.foreground.isGrantable || group.background.isGrantable)) {
98             return false
99         }
100         val isPlatformPermission = group.packageName == Utils.OS_PKG
101         // Show legacy permissions only if the user chose that.
102         return !(isPlatformPermission && !Utils.isModernPermissionGroup(group.permGroupName))
103     }
104 
105     fun isPackageUpdated(): Boolean {
106         val permGroupsMap: Map<String, LightAppPermGroup> = permissionGroupsLiveData.value!!
107         return permGroupsMap.any { !it.value.isReviewRequired }
108     }
109 
110     /**
111      * Update the summary of a permission group that has background permission.
112      * This does not apply to permission groups that are fixed by policy
113      */
114     fun getSummaryForPermGroupWithBackgroundPermission(
115         state: PermissionTarget
116     ): PermissionSummary {
117         if (state != PermissionTarget.PERMISSION_NONE) {
118             if (state.and(PermissionTarget.PERMISSION_BACKGROUND)
119                 != PermissionTarget.PERMISSION_NONE.value) {
120                 return SummaryMessage.ACCESS_ALWAYS.toPermSummary()
121             } else {
122                 return SummaryMessage.ACCESS_ONLY_FOREGROUND.toPermSummary()
123             }
124         } else {
125             return SummaryMessage.ACCESS_NEVER.toPermSummary()
126         }
127     }
128 
129     fun getSummaryForIndividuallyControlledPermGroup(
130         permGroup: LightAppPermGroup
131     ): PermissionSummary {
132         var revokedCount = 0
133         val lightPerms = permGroup.allPermissions.values.toList()
134         val permissionCount = lightPerms.size
135         for (i in 0 until permissionCount) {
136             if (!lightPerms[i].isGrantedIncludingAppOp) {
137                 revokedCount++
138             }
139         }
140         return when (revokedCount) {
141             0 -> {
142                 SummaryMessage.REVOKED_NONE.toPermSummary()
143             }
144             permissionCount -> {
145                 SummaryMessage.REVOKED_ALL.toPermSummary()
146             }
147             else -> {
148                 PermissionSummary(SummaryMessage.REVOKED_COUNT, false, revokedCount)
149             }
150         }
151     }
152 
153     /**
154      * Show all individual permissions in this group in a new fragment.
155      */
156     fun showAllPermissions(fragment: Fragment, args: Bundle) {
157         val navController: NavController = NavHostFragment.findNavController(fragment)
158         navController.navigateSafe(R.id.app_to_all_perms, args)
159     }
160 
161     enum class SummaryMessage {
162         NO_SUMMARY,
163         DISABLED_BY_ADMIN,
164         ENABLED_BY_ADMIN,
165         ENABLED_SYSTEM_FIXED,
166         ENFORCED_BY_POLICY,
167         ENABLED_BY_ADMIN_FOREGROUND_ONLY,
168         ENABLED_BY_POLICY_FOREGROUND_ONLY,
169         ENABLED_BY_ADMIN_BACKGROUND_ONLY,
170         ENABLED_BY_POLICY_BACKGROUND_ONLY,
171         DISABLED_BY_ADMIN_BACKGROUND_ONLY,
172         DISABLED_BY_POLICY_BACKGROUND_ONLY,
173         REVOKED_NONE,
174         REVOKED_ALL,
175         REVOKED_COUNT,
176         ACCESS_ALWAYS,
177         ACCESS_ONLY_FOREGROUND,
178         ACCESS_NEVER;
179 
180         fun toPermSummary(): PermissionSummary {
181             return PermissionSummary(this, false)
182         }
183 
184         fun toPermSummary(isEnterprise: Boolean): PermissionSummary {
185             return PermissionSummary(this, isEnterprise)
186         }
187     }
188 
189     data class PermissionSummary(
190         val msg: SummaryMessage,
191         val isEnterprise: Boolean = false,
192         val revokeCount: Int = 0
193     )
194 
195     fun getSummaryForFixedByPolicyPermissionGroup(
196         mState: PermissionTarget,
197         permGroup: LightAppPermGroup,
198         context: Context
199     ): PermissionSummary {
200         val admin = getAdmin(context, permGroup)
201         val hasAdmin = admin != null
202         if (permGroup.isSystemFixed) {
203             // Permission is fully controlled by the system and cannot be switched
204             return SummaryMessage.ENABLED_SYSTEM_FIXED.toPermSummary()
205         } else if (isForegroundDisabledByPolicy(permGroup)) {
206             // Permission is fully controlled by policy and cannot be switched
207             return if (hasAdmin) {
208                 SummaryMessage.DISABLED_BY_ADMIN.toPermSummary()
209             } else {
210                 // Disabled state will be displayed by switch, so no need to add text for that
211                 SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
212             }
213         } else if (permGroup.isPolicyFullyFixed) {
214             // Permission is fully controlled by policy and cannot be switched
215             if (!permGroup.hasBackgroundGroup) {
216                 return if (hasAdmin) {
217                     SummaryMessage.ENABLED_BY_ADMIN.toPermSummary()
218                 } else {
219                     // Enabled state will be displayed by switch, so no need to add text for that
220                     SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
221                 }
222             } else {
223                 if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
224                     PermissionTarget.PERMISSION_NONE.value) {
225                     return if (hasAdmin) {
226                         SummaryMessage.ENABLED_BY_ADMIN.toPermSummary()
227                     } else {
228                         // Enabled state will be displayed by switch, so no need to add text for
229                         // that
230                         SummaryMessage.ENFORCED_BY_POLICY.toPermSummary()
231                     }
232                 } else {
233                     return if (hasAdmin) {
234                         SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary()
235                     } else {
236                         SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
237                     }
238                 }
239             }
240         } else {
241             // Part of the permission group can still be switched
242             if (permGroup.background.isPolicyFixed) {
243                 return if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) !=
244                     PermissionTarget.PERMISSION_NONE.value) {
245                     if (hasAdmin) {
246                         SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true)
247                     } else {
248                         SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
249                     }
250                 } else {
251                     if (hasAdmin) {
252                         SummaryMessage.DISABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true)
253                     } else {
254                         SummaryMessage.DISABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary()
255                     }
256                 }
257             } else if (permGroup.foreground.isPolicyFixed) {
258                 return if (hasAdmin) {
259                     SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(true)
260                 } else {
261                     SummaryMessage.ENABLED_BY_POLICY_FOREGROUND_ONLY.toPermSummary()
262                 }
263             }
264         }
265         return SummaryMessage.NO_SUMMARY.toPermSummary()
266     }
267 
268     /**
269      * Is the foreground part of this group disabled. If the foreground is disabled, there is no
270      * need to possible grant background access.
271      *
272      * @return `true` iff the permissions of this group are fixed
273      */
274     private fun isForegroundDisabledByPolicy(mGroup: LightAppPermGroup): Boolean {
275         return mGroup.foreground.isPolicyFixed && !mGroup.isGranted
276     }
277 
278     /**
279      * Whether policy is system fixed or fully fixed or foreground disabled
280      */
281     fun isFixedOrForegroundDisabled(mGroup: LightAppPermGroup): Boolean {
282         return mGroup.isSystemFixed || mGroup.isPolicyFullyFixed ||
283             isForegroundDisabledByPolicy(mGroup)
284     }
285 
286     /**
287      * Get the app that acts as admin for this profile.
288      *
289      * @return The admin or `null` if there is no admin.
290      */
291     fun getAdmin(context: Context, mGroup: LightAppPermGroup): EnforcedAdmin? {
292         return RestrictedLockUtils.getProfileOrDeviceOwner(context, mGroup.userHandle)
293     }
294 
295     enum class PermissionTarget(val value: Int) {
296         PERMISSION_NONE(0),
297         PERMISSION_FOREGROUND(1),
298         PERMISSION_BACKGROUND(2),
299         PERMISSION_BOTH(3);
300 
301         infix fun and(other: PermissionTarget): Int {
302             return value and other.value
303         }
304 
305         infix fun and(other: Int): Int {
306             return value and other
307         }
308 
309         infix fun or(other: PermissionTarget): Int {
310             return value or other.value
311         }
312 
313         companion object {
314             fun fromInt(value: Int) = values().first { it.value == value }
315         }
316     }
317 }
318 
319 class ReviewPermissionViewModelFactory(
320     private val app: Application,
321     private val packageInfo: PackageInfo
322 ) : ViewModelProvider.Factory {
createnull323     override fun <T : ViewModel> create(modelClass: Class<T>): T {
324         @Suppress("UNCHECKED_CAST")
325         return ReviewPermissionsViewModel(app, packageInfo = packageInfo) as T
326     }
327 }