• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 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.AppOpsManager
20 import android.app.AppOpsManager.MODE_ALLOWED
21 import android.app.AppOpsManager.MODE_IGNORED
22 import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
23 import android.Manifest
24 import android.app.role.RoleManager
25 import android.content.Context.MODE_PRIVATE
26 import android.os.Bundle
27 import android.os.UserHandle
28 import android.util.Log
29 import androidx.fragment.app.Fragment
30 import androidx.lifecycle.ViewModel
31 import androidx.lifecycle.ViewModelProvider
32 import androidx.navigation.fragment.findNavController
33 import com.android.permissioncontroller.Constants.ASSISTANT_RECORD_AUDIO_IS_USER_SENSITIVE_KEY
34 import com.android.permissioncontroller.Constants.PREFERENCES_FILE
35 import com.android.permissioncontroller.PermissionControllerApplication
36 import com.android.permissioncontroller.PermissionControllerStatsLog
37 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION
38 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
39 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
40 import com.android.permissioncontroller.R
41 import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
42 import com.android.permissioncontroller.permission.data.AutoRevokeStateLiveData
43 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
44 import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
45 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
46 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
47 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
48 import com.android.permissioncontroller.permission.data.get
49 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
50 import com.android.permissioncontroller.permission.ui.Category
51 import com.android.permissioncontroller.permission.utils.IPC
52 import com.android.permissioncontroller.permission.utils.Utils
53 import com.android.permissioncontroller.permission.utils.navigateSafe
54 import kotlinx.coroutines.GlobalScope
55 import kotlinx.coroutines.launch
56 
57 /**
58  * ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all
59  * permission groups that this package requests runtime permissions from
60  *
61  * @param packageName The name of the package this viewModel is representing
62  * @param user The user of the package this viewModel is representing
63  */
64 class AppPermissionGroupsViewModel(
65     private val packageName: String,
66     private val user: UserHandle,
67     private val sessionId: Long
68 ) : ViewModel() {
69 
70     companion object {
71         val LOG_TAG: String = AppPermissionGroupsViewModel::class.java.simpleName
72     }
73 
74     val app = PermissionControllerApplication.get()!!
75 
76     enum class PermSubtitle(val value: Int) {
77         NONE(0),
78         MEDIA_ONLY(1),
79         ALL_FILES(2),
80         FOREGROUND_ONLY(3),
81     }
82 
83     data class GroupUiInfo(
84         val groupName: String,
85         val isSystem: Boolean = false,
86         val subtitle: PermSubtitle
87     ) {
88         constructor(groupName: String, isSystem: Boolean) :
89             this(groupName, isSystem, PermSubtitle.NONE)
90     }
91 
92     val autoRevokeLiveData = AutoRevokeStateLiveData[packageName, user]
93 
94     /**
95      * LiveData whose data is a map of grant category (either allowed or denied) to a list
96      * of permission group names that match the key, and two booleans representing if this is a
97      * system group, and a subtitle resource ID, if applicable.
98      */
99     val packagePermGroupsLiveData = object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards
100     Map<Category, List<GroupUiInfo>>>() {
101 
102         private val packagePermsLiveData =
103             PackagePermissionsLiveData[packageName, user]
104         private val appPermGroupUiInfoLiveDatas = mutableMapOf<String, AppPermGroupUiInfoLiveData>()
105         private val fullStoragePermsLiveData = FullStoragePermissionAppsLiveData
106 
107         init {
108             addSource(packagePermsLiveData) {
109                 updateIfActive()
110             }
111             addSource(fullStoragePermsLiveData) {
112                 updateIfActive()
113             }
114             addSource(autoRevokeLiveData) {
115                 removeSource(autoRevokeLiveData)
116                 updateIfActive()
117             }
118             updateIfActive()
119         }
120 
121         override fun onUpdate() {
122             val groups = packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS }
123             if (groups == null && packagePermsLiveData.isInitialized) {
124                 value = null
125                 return
126             } else if (groups == null || (Manifest.permission_group.STORAGE in groups &&
127                     !fullStoragePermsLiveData.isInitialized) || !autoRevokeLiveData.isInitialized) {
128                 return
129             }
130 
131             val hasFullStorage = fullStoragePermsLiveData.value?.any { pkg ->
132                 pkg.packageName == packageName && pkg.user == user && pkg.isGranted
133             } ?: false
134 
135             val getLiveData = { groupName: String ->
136                 AppPermGroupUiInfoLiveData[packageName, groupName, user]
137             }
138             setSourcesToDifference(groups, appPermGroupUiInfoLiveDatas, getLiveData)
139 
140             if (!appPermGroupUiInfoLiveDatas.all { it.value.isInitialized }) {
141                 return
142             }
143 
144             val groupGrantStates = mutableMapOf<Category,
145                 MutableList<GroupUiInfo>>()
146             groupGrantStates[Category.ALLOWED] = mutableListOf()
147             groupGrantStates[Category.ASK] = mutableListOf()
148             groupGrantStates[Category.DENIED] = mutableListOf()
149 
150             for (groupName in groups) {
151                 val isSystem = Utils.getPlatformPermissionGroups().contains(groupName)
152                 appPermGroupUiInfoLiveDatas[groupName]?.value?.let { uiInfo ->
153                     when (uiInfo.permGrantState) {
154                         PermGrantState.PERMS_ALLOWED -> {
155                             var subtitle = PermSubtitle.NONE
156                             if (groupName == Manifest.permission_group.STORAGE) {
157                                 subtitle = if (hasFullStorage) {
158                                     PermSubtitle.ALL_FILES
159                                 } else {
160                                     PermSubtitle.MEDIA_ONLY
161                                 }
162                             }
163                             groupGrantStates[Category.ALLOWED]!!.add(
164                                 GroupUiInfo(groupName, isSystem, subtitle))
165                         }
166                         PermGrantState.PERMS_ALLOWED_ALWAYS -> groupGrantStates[
167                             Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem))
168                         PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY -> groupGrantStates[
169                             Category.ALLOWED]!!.add(GroupUiInfo(groupName, isSystem,
170                             PermSubtitle.FOREGROUND_ONLY))
171                         PermGrantState.PERMS_DENIED -> groupGrantStates[Category.DENIED]!!.add(
172                             GroupUiInfo(groupName, isSystem))
173                         PermGrantState.PERMS_ASK -> groupGrantStates[Category.ASK]!!.add(
174                             GroupUiInfo(groupName, isSystem))
175                     }
176                 }
177             }
178 
179             value = groupGrantStates
180         }
181     }
182 
183     fun setAutoRevoke(enabled: Boolean) {
184         GlobalScope.launch(IPC) {
185             val aom = app.getSystemService(AppOpsManager::class.java)!!
186             val uid = LightPackageInfoLiveData[packageName, user].getInitializedValue()?.uid
187 
188             if (uid != null) {
189                 Log.i(LOG_TAG, "sessionId $sessionId setting auto revoke enabled to $enabled for" +
190                     "$packageName $user")
191                 val tag = if (enabled) {
192                     APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
193                 } else {
194                     APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_DISABLED
195                 }
196                 PermissionControllerStatsLog.write(
197                     APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION, sessionId, uid, packageName,
198                     tag)
199 
200                 val mode = if (enabled) {
201                     MODE_ALLOWED
202                 } else {
203                     MODE_IGNORED
204                 }
205                 aom.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, mode)
206             }
207         }
208     }
209 
210     fun showExtraPerms(fragment: Fragment, args: Bundle) {
211         fragment.findNavController().navigateSafe(R.id.perm_groups_to_custom, args)
212     }
213 
214     fun showAllPermissions(fragment: Fragment, args: Bundle) {
215         fragment.findNavController().navigateSafe(R.id.perm_groups_to_all_perms, args)
216     }
217 }
218 
219 /**
220  * Factory for an AppPermissionGroupsViewModel
221  *
222  * @param packageName The name of the package this viewModel is representing
223  * @param user The user of the package this viewModel is representing
224  */
225 class AppPermissionGroupsViewModelFactory(
226     private val packageName: String,
227     private val user: UserHandle,
228     private val sessionId: Long
229 ) : ViewModelProvider.Factory {
230 
createnull231     override fun <T : ViewModel> create(modelClass: Class<T>): T {
232         @Suppress("UNCHECKED_CAST")
233         return AppPermissionGroupsViewModel(packageName, user, sessionId) as T
234     }
235 }
236