1 /*
2  * 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.Manifest
20 import android.Manifest.permission.ACCESS_COARSE_LOCATION
21 import android.Manifest.permission.ACCESS_FINE_LOCATION
22 import android.Manifest.permission_group.LOCATION
23 import android.app.Activity
24 import android.app.Application
25 import android.app.admin.DevicePolicyManager
26 import android.content.Intent
27 import android.content.pm.PackageManager
28 import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED
29 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED
30 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
31 import android.os.Build
32 import android.os.Bundle
33 import android.os.Process
34 import android.permission.PermissionManager
35 import android.util.Log
36 import androidx.core.util.Consumer
37 import androidx.lifecycle.ViewModel
38 import androidx.lifecycle.ViewModelProvider
39 import com.android.permissioncontroller.Constants
40 import com.android.permissioncontroller.PermissionControllerStatsLog
41 import com.android.permissioncontroller.PermissionControllerStatsLog.GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS
42 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED
43 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED
44 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED
45 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED
46 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION
47 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED
48 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
49 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
50 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
51 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS
52 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
53 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS
54 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
55 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
56 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
57 import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
58 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
59 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
60 import com.android.permissioncontroller.permission.data.get
61 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
62 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
63 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo
64 import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier
65 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity
66 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
67 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON
68 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON
69 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON
70 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON
71 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON
72 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS
73 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY
74 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY
75 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON
76 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_SETTINGS
77 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT
78 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON
79 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG
80 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON
81 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON
82 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
83 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON
84 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.PERMISSION_TO_BIT_SHIFT
85 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler
86 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED
87 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN
88 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS
89 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY
90 import com.android.permissioncontroller.permission.ui.handheld.dashboard.getDefaultPrecision
91 import com.android.permissioncontroller.permission.ui.handheld.dashboard.isLocationAccuracyEnabled
92 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
93 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED
94 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT
95 import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils
96 import com.android.permissioncontroller.permission.utils.KotlinUtils
97 import com.android.permissioncontroller.permission.utils.SafetyNetLogger
98 import com.android.permissioncontroller.permission.utils.Utils
99 import kotlinx.coroutines.Dispatchers.Main
100 import kotlinx.coroutines.GlobalScope
101 import kotlinx.coroutines.launch
102 
103 /**
104  * ViewModel for the GrantPermissionsActivity. Tracks all permission groups that are affected by
105  * the permissions requested by the user, and generates a RequestInfo object for each group, if
106  * action is needed. It will not return any data if one of the requests is malformed.
107  *
108  * @param app: The current application
109  * @param packageName: The packageName permissions are being requested for
110  * @param requestedPermissions: The list of permissions requested
111  * @param sessionId: A long to identify this session
112  * @param storedState: Previous state, if this activity was stopped and is being recreated
113  */
114 class GrantPermissionsViewModel(
115     private val app: Application,
116     private val packageName: String,
117     private val requestedPermissions: List<String>,
118     private val sessionId: Long,
119     private val storedState: Bundle?
120 ) : ViewModel() {
121     private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
122     private val user = Process.myUserHandle()
123     private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
124     private val dpm = app.getSystemService(DevicePolicyManager::class.java)!!
125     private val permissionPolicy = dpm.getPermissionPolicy(null)
126     private val permGroupsToSkip = mutableListOf<String>()
127     private var groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
128     private var isFirstTimeRequestingFineAndCoarse: Boolean = false
129 
130     private var autoGrantNotifier: AutoGrantPermissionsNotifier? = null
131     private fun getAutoGrantNotifier(): AutoGrantPermissionsNotifier {
132         autoGrantNotifier = AutoGrantPermissionsNotifier(app, packageInfo.toPackageInfo(app)!!)
133         return autoGrantNotifier!!
134     }
135 
136     private lateinit var packageInfo: LightPackageInfo
137 
138     // All permissions that could possibly be affected by the provided requested permissions, before
139     // filtering system fixed, auto grant, etc.
140     private var unfilteredAffectedPermissions = requestedPermissions
141 
142     /**
143      * A class which represents a correctly requested permission group, and the buttons and messages
144      * which should be shown with it.
145      */
146     data class RequestInfo(
147         val groupInfo: LightPermGroupInfo,
148         val buttonVisibilities: List<Boolean> = List(NEXT_BUTTON) { false },
149         val locationVisibilities: List<Boolean> = List(NEXT_LOCATION_DIALOG) { false },
150         val message: RequestMessage = RequestMessage.FG_MESSAGE,
151         val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE,
152         val sendToSettingsImmediately: Boolean = false
153     ) {
154         val groupName = groupInfo.name
155     }
156 
157     var activityResultCallback: Consumer<Intent>? = null
158 
159     /**
160      * A LiveData which holds a list of the currently pending RequestInfos
161      */
162     val requestInfosLiveData = object :
163         SmartUpdateMediatorLiveData<List<RequestInfo>>() {
164         private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
165         private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user]
166         private val appPermGroupLiveDatas = mutableMapOf<String, LightAppPermGroupLiveData>()
167 
168         init {
169             GlobalScope.launch(Main.immediate) {
170                 val groups = packagePermissionsLiveData.getInitializedValue()
171                 if (groups == null || groups.isEmpty()) {
172                     Log.e(LOG_TAG, "Package $packageName not found")
173                     value = null
174                     return@launch
175                 }
176                 packageInfo = packageInfoLiveData.getInitializedValue()
177 
178                 if (packageInfo.requestedPermissions.isEmpty() ||
179                     packageInfo.targetSdkVersion < Build.VERSION_CODES.M) {
180                     Log.e(LOG_TAG, "Package $packageName has no requested permissions, or " +
181                         "is a pre-M app")
182                     value = null
183                     return@launch
184                 }
185 
186                 val allAffectedPermissions = requestedPermissions.toMutableSet()
187                 for (requestedPerm in requestedPermissions) {
188                     allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups))
189                 }
190                 unfilteredAffectedPermissions = allAffectedPermissions.toList()
191 
192                 getAppPermGroups(groups.toMutableMap().apply {
193                         remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
194                     })
195             }
196         }
197 
198         private fun getAppPermGroups(groups: Map<String, List<String>>) {
199 
200             val requestedGroups = groups.filter { (_, perms) ->
201                 perms.any { it in unfilteredAffectedPermissions }
202             }
203 
204             if (requestedGroups.isEmpty()) {
205                 Log.e(LOG_TAG, "None of " +
206                     "$unfilteredAffectedPermissions in $groups")
207                 value = null
208                 return
209             }
210 
211             val getLiveDataFun = { groupName: String ->
212                 LightAppPermGroupLiveData[packageName, groupName, user]
213             }
214             setSourcesToDifference(requestedGroups.keys, appPermGroupLiveDatas, getLiveDataFun)
215         }
216 
217         override fun onUpdate() {
218             if (appPermGroupLiveDatas.any { it.value.isStale }) {
219                 return
220             }
221             var newGroups = false
222             for ((groupName, groupLiveData) in appPermGroupLiveDatas) {
223                 val appPermGroup = groupLiveData.value
224                 if (appPermGroup == null || groupName in permGroupsToSkip) {
225                     if (appPermGroup == null) {
226                         Log.e(LOG_TAG, "Group $packageName $groupName invalid")
227                     }
228                     groupStates[groupName to true]?.state = STATE_SKIPPED
229                     groupStates[groupName to false]?.state = STATE_SKIPPED
230                     continue
231                 }
232 
233                 packageInfo = appPermGroup.packageInfo
234 
235                 val states = groupStates.filter { it.key.first == groupName }
236                 if (states.isNotEmpty()) {
237                     // some requests might have been granted, check for that
238                     for ((key, state) in states) {
239                         val allAffectedGranted = state.affectedPermissions.all { perm ->
240                             appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true
241                         }
242                         if (allAffectedGranted) {
243                             groupStates[key]!!.state = STATE_ALLOWED
244                         }
245                     }
246                 } else {
247                     newGroups = true
248                 }
249             }
250 
251             if (newGroups) {
252                 groupStates = getRequiredGroupStates(
253                     appPermGroupLiveDatas.mapNotNull { it.value.value })
254             }
255             getRequestInfosFromGroupStates()
256         }
257 
258         private fun getRequestInfosFromGroupStates() {
259             val requestInfos = mutableListOf<RequestInfo>()
260             for ((key, groupState) in groupStates) {
261                 val groupInfo = groupState.group.permGroupInfo
262                 val (groupName, isBackground) = key
263                 if (groupState.state != STATE_UNKNOWN) {
264                     continue
265                 }
266 
267                 val fgState = groupStates[groupName to false]
268                 val bgState = groupStates[groupName to true]
269                 var needFgPermissions = false
270                 var needBgPermissions = false
271                 var isFgUserSet = false
272                 var isBgUserSet = false
273 
274                 if (fgState?.group != null) {
275                     val fgGroup = fgState.group
276                     for (perm in fgState.affectedPermissions) {
277                         if (fgGroup.permissions[perm]?.isGrantedIncludingAppOp == false) {
278                             // If any of the requested permissions is not granted,
279                             // needFgPermissions = true
280                             needFgPermissions = true
281                             // If any of the requested permission's UserSet is true and the
282                             // permission is not granted, isFgUserSet = true.
283                             if (fgGroup.permissions[perm]?.isUserSet == true) {
284                                 isFgUserSet = true
285                             }
286                         }
287                     }
288                 }
289 
290                 if (bgState?.group?.background?.isGranted == false) {
291                     needBgPermissions = true
292                     isBgUserSet = bgState.group.background.isUserSet
293                 }
294 
295                 val buttonVisibilities = MutableList(NEXT_BUTTON) { false }
296                 buttonVisibilities[ALLOW_BUTTON] = true
297                 buttonVisibilities[DENY_BUTTON] = true
298                 buttonVisibilities[ALLOW_ONE_TIME_BUTTON] =
299                     Utils.supportsOneTimeGrant(groupName)
300                 var message = RequestMessage.FG_MESSAGE
301                 // Whether or not to use the foreground, background, or no detail message.
302                 // null ==
303                 var detailMessage = RequestMessage.NO_MESSAGE
304 
305                 if (groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.R) {
306                     if (isBackground || groupState.group.hasPermWithBackgroundMode) {
307                         if (needFgPermissions) {
308                             if (needBgPermissions) {
309                                 if (groupState.group.permGroupName
310                                                 .equals(Manifest.permission_group.CAMERA) ||
311                                         groupState.group.permGroupName
312                                                 .equals(Manifest.permission_group.MICROPHONE)) {
313                                     if (groupState.group.packageInfo.targetSdkVersion >=
314                                             Build.VERSION_CODES.S) {
315                                         Log.e(LOG_TAG,
316                                                 "For S apps, background permissions must be " +
317                                                 "requested after foreground permissions are" +
318                                                         " already granted")
319                                         value = null
320                                         return
321                                     } else {
322                                         // Case: sdk < S, BG&FG mic/camera permission requested
323                                         buttonVisibilities[ALLOW_BUTTON] = false
324                                         buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
325                                         buttonVisibilities[DENY_BUTTON] = !isFgUserSet
326                                         buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] =
327                                                 isFgUserSet
328                                         if (needBgPermissions) {
329                                             // Case: sdk < R, BG/FG permission requesting both
330                                             message = RequestMessage.BG_MESSAGE
331                                             detailMessage = RequestMessage.BG_MESSAGE
332                                         }
333                                     }
334                                 } else {
335                                     // Shouldn't be reached as background must be requested as a
336                                     // singleton
337                                     Log.e(LOG_TAG, "For R+ apps, background permissions must be " +
338                                             "requested after foreground permissions are already" +
339                                             " granted")
340                                     value = null
341                                     return
342                                 }
343                             } else {
344                                 buttonVisibilities[ALLOW_BUTTON] = false
345                                 buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
346                                 buttonVisibilities[DENY_BUTTON] = !isFgUserSet
347                                 buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
348                             }
349                         } else if (needBgPermissions) {
350                             // Case: sdk >= R, BG/FG permission requesting BG only
351                             requestInfos.add(RequestInfo(
352                                 groupInfo, sendToSettingsImmediately = true))
353                             continue
354                         } else {
355                             // Not reached as the permissions should be auto-granted
356                             value = null
357                             return
358                         }
359                     } else {
360                         // Case: sdk >= R, Requesting normal permission
361                         buttonVisibilities[DENY_BUTTON] = !isFgUserSet
362                         buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
363                     }
364                 } else {
365                     if (isBackground || groupState.group.hasPermWithBackgroundMode) {
366                         if (needFgPermissions) {
367                             // Case: sdk < R, BG/FG permission requesting both or FG only
368                             buttonVisibilities[ALLOW_BUTTON] = false
369                             buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = true
370                             buttonVisibilities[DENY_BUTTON] = !isFgUserSet
371                             buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
372                             if (needBgPermissions) {
373                                 // Case: sdk < R, BG/FG permission requesting both
374                                 message = RequestMessage.BG_MESSAGE
375                                 detailMessage = RequestMessage.BG_MESSAGE
376                             }
377                         } else if (needBgPermissions) {
378                             // Case: sdk < R, BG/FG permission requesting BG only
379                             if (!groupState.group.foreground.isGranted) {
380                                 Log.e(LOG_TAG, "Background permissions can't be requested " +
381                                         "solely before foreground permissions are granted.")
382                                 value = null
383                                 return
384                             }
385                             message = RequestMessage.UPGRADE_MESSAGE
386                             detailMessage = RequestMessage.UPGRADE_MESSAGE
387                             buttonVisibilities[ALLOW_BUTTON] = false
388                             buttonVisibilities[DENY_BUTTON] = false
389                             buttonVisibilities[ALLOW_ONE_TIME_BUTTON] = false
390                             if (groupState.group.isOneTime) {
391                                 buttonVisibilities[NO_UPGRADE_OT_BUTTON] = !isBgUserSet
392                                 buttonVisibilities[NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON] =
393                                     isBgUserSet
394                             } else {
395                                 buttonVisibilities[NO_UPGRADE_BUTTON] = !isBgUserSet
396                                 buttonVisibilities[NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON] =
397                                     isBgUserSet
398                             }
399                         } else {
400                             // Not reached as the permissions should be auto-granted
401                             value = null
402                             return
403                         }
404                     } else {
405                         // If no permissions needed, do nothing
406                         if (!needFgPermissions && !needBgPermissions) {
407                             value = null
408                             return
409                         }
410                         // Case: sdk < R, Requesting normal permission
411                         buttonVisibilities[DENY_BUTTON] = !isFgUserSet
412                         buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON] = isFgUserSet
413                     }
414                 }
415                 buttonVisibilities[LINK_TO_SETTINGS] =
416                     detailMessage != RequestMessage.NO_MESSAGE
417 
418                 // Show location permission dialogs based on location permissions
419                 val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
420                 if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() &&
421                         packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
422                     if (needFgPermissions) {
423                         locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
424                         if (fgState != null &&
425                                 fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
426                             val coarseLocationPerm =
427                                 groupState.group.allPermissions[ACCESS_COARSE_LOCATION]
428                             if (coarseLocationPerm?.isGrantedIncludingAppOp == true) {
429                                 // Upgrade flow
430                                 locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true
431                                 message = RequestMessage.FG_FINE_LOCATION_MESSAGE
432                                 // If COARSE was granted one time, hide 'While in use' button
433                                 if (coarseLocationPerm.isOneTime) {
434                                     buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
435                                 }
436                             } else {
437                                 if (!fgState.affectedPermissions.contains(ACCESS_COARSE_LOCATION)) {
438                                     Log.e(LOG_TAG, "ACCESS_FINE_LOCATION must be requested " +
439                                             "with ACCESS_COARSE_LOCATION.")
440                                     value = null
441                                     return
442                                 }
443                                 if (coarseLocationPerm?.isOneTime == false &&
444                                         !coarseLocationPerm.isUserSet &&
445                                         !coarseLocationPerm.isUserFixed) {
446                                     isFirstTimeRequestingFineAndCoarse = true
447                                 }
448                                 // Normal flow with both Coarse and Fine locations
449                                 locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true
450                                 // Steps to decide location accuracy default state
451                                 // 1. If none of the FINE and COARSE isSelectedLocationAccuracy
452                                 //    flags is set, then use default precision from device config.
453                                 // 2. Otherwise set to whichever isSelectedLocationAccuracy is true.
454                                 val fineLocationPerm =
455                                         groupState.group.allPermissions[ACCESS_FINE_LOCATION]
456                                 if (coarseLocationPerm?.isSelectedLocationAccuracy == false &&
457                                         fineLocationPerm?.isSelectedLocationAccuracy == false) {
458                                     if (getDefaultPrecision()) {
459                                         locationVisibilities[FINE_RADIO_BUTTON] = true
460                                     } else {
461                                         locationVisibilities[COARSE_RADIO_BUTTON] = true
462                                     }
463                                 } else if (coarseLocationPerm?.isSelectedLocationAccuracy == true) {
464                                     locationVisibilities[COARSE_RADIO_BUTTON] = true
465                                 } else {
466                                     locationVisibilities[FINE_RADIO_BUTTON] = true
467                                 }
468                             }
469                         } else if (fgState != null && fgState.affectedPermissions
470                                         .contains(ACCESS_COARSE_LOCATION)) {
471                             // Request Coarse only
472                             locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true
473                             message = RequestMessage.FG_COARSE_LOCATION_MESSAGE
474                         }
475                     }
476                 }
477 
478                 requestInfos.add(RequestInfo(
479                     groupInfo,
480                     buttonVisibilities,
481                     locationVisibilities,
482                     message,
483                     detailMessage))
484             }
485             requestInfos.sortWith(Comparator { rhs, lhs ->
486                 val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
487                 val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
488                 if (rhsHasOneTime && !lhsHasOneTime) {
489                     -1
490                 } else if (!rhsHasOneTime && lhsHasOneTime) {
491                     1
492                 } else {
493                     rhs.groupName.compareTo(lhs.groupName)
494                 }
495             })
496 
497             value = if (requestInfos.any { it.sendToSettingsImmediately } &&
498                 requestInfos.size > 1) {
499                 Log.e(LOG_TAG, "For R+ apps, background permissions must be requested " +
500                     "individually")
501                 null
502             } else {
503                 requestInfos
504             }
505         }
506     }
507 
508     /**
509      * Converts a list of LightAppPermGroups into a list of GroupStates
510      */
511     private fun getRequiredGroupStates(
512         groups: List<LightAppPermGroup>
513     ): MutableMap<Pair<String, Boolean>, GroupState> {
514         val groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
515         val filteredPermissions = unfilteredAffectedPermissions.filter { perm ->
516             val group = getGroupWithPerm(perm, groups)
517             group != null && isPermissionGrantableAndNotFixed(perm, group)
518         }
519         for (perm in filteredPermissions) {
520             val group = getGroupWithPerm(perm, groups)!!
521 
522             val isBackground = perm in group.backgroundPermNames
523             val groupStateInfo = groupStates.getOrPut(group.permGroupName to isBackground) {
524                 GroupState(group, isBackground)
525             }
526 
527             var currGroupState = groupStateInfo.state
528             if (storedState != null && currGroupState != STATE_UNKNOWN) {
529                 currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName,
530                     isBackground), STATE_UNKNOWN)
531             }
532 
533             val otherGroupPermissions = filteredPermissions.filter { it in group.permissions }
534             val groupStateOfPerm = getGroupState(perm, group, otherGroupPermissions)
535             if (groupStateOfPerm != STATE_UNKNOWN) {
536                 currGroupState = groupStateOfPerm
537             }
538 
539             if (group.permGroupName in permGroupsToSkip) {
540                 currGroupState = STATE_SKIPPED
541             }
542 
543             if (currGroupState != STATE_UNKNOWN) {
544                 groupStateInfo.state = currGroupState
545             }
546             // If we saved state, load it
547             groupStateInfo.affectedPermissions.add(perm)
548         }
549         return groupStates
550     }
551 
552     /**
553      * Get the actually requested permissions when a permission is requested.
554      *
555      * >In some cases requesting to grant a single permission requires the system to grant
556      * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
557      * group to be granted. Another case are permissions that are split into two. For apps that
558      * target an SDK before the split, this method automatically adds the split off permission.
559      *
560      * @param perm The requested permission
561      *
562      * @return The actually requested permissions
563      */
564     private fun computeAffectedPermissions(
565         perm: String,
566         appPermissions: Map<String, List<String>>
567     ): List<String> {
568         val requestingAppTargetSDK = packageInfo.targetSdkVersion
569 
570         // If a permission is split, all permissions the original permission is split into are
571         // affected
572         val extendedBySplitPerms = mutableListOf(perm)
573 
574         val splitPerms = app.getSystemService(PermissionManager::class.java)!!.splitPermissions
575         for (splitPerm in splitPerms) {
576 
577             if (requestingAppTargetSDK < splitPerm.targetSdk && perm == splitPerm.splitPermission) {
578                 extendedBySplitPerms.addAll(splitPerm.newPermissions)
579             }
580         }
581 
582         // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
583         if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
584             val extendedBySplitPermsAndGroup = mutableListOf<String>()
585 
586             for (splitPerm in extendedBySplitPerms) {
587                 val groups = appPermissions.filter { splitPerm in it.value }
588                 if (groups.isEmpty()) {
589                     continue
590                 }
591 
592                 val permissionsInGroup = groups.values.first()
593                 for (permissionInGroup in permissionsInGroup) {
594                     extendedBySplitPermsAndGroup.add(permissionInGroup)
595                 }
596             }
597 
598             return extendedBySplitPermsAndGroup
599         } else {
600             return extendedBySplitPerms
601         }
602     }
603 
604     private fun isPermissionGrantableAndNotFixed(perm: String, group: LightAppPermGroup): Boolean {
605 
606         // If the permission is restricted it does not show in the UI and
607         // is not added to the group at all, so check that first.
608         if (perm in group.packageInfo.requestedPermissions && perm !in group.permissions) {
609             reportRequestResult(perm,
610                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION)
611             return false
612         }
613 
614         val subGroup = if (perm in group.backgroundPermNames) {
615             group.background
616         } else {
617             group.foreground
618         }
619 
620         val lightPermission = group.permissions[perm] ?: return false
621 
622         if (!subGroup.isGrantable) {
623             reportRequestResult(perm, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
624             // Skip showing groups that we know cannot be granted.
625             return false
626         } else if (subGroup.isUserFixed) {
627             reportRequestResult(perm,
628                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED)
629             return false
630         } else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) {
631             reportRequestResult(perm,
632                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED)
633             return false
634         }
635 
636         return true
637     }
638 
639     private fun getGroupState(
640         perm: String,
641         group: LightAppPermGroup,
642         groupRequestedPermissions: List<String>
643     ): Int {
644         val policyState = getStateFromPolicy(perm, group)
645         if (policyState != STATE_UNKNOWN) {
646             return policyState
647         }
648 
649         val isBackground = perm in group.backgroundPermNames
650 
651         val hasForegroundRequest = groupRequestedPermissions.any {
652             it !in group.backgroundPermNames
653         }
654 
655         // Do not attempt to grant background access if foreground access is not either already
656         // granted or requested
657         if (isBackground && !group.foreground.isGranted && !hasForegroundRequest) {
658             Log.w(LOG_TAG, "Cannot grant $perm as the matching foreground permission is not " +
659                 "already granted.")
660             val affectedPermissions = groupRequestedPermissions.filter {
661                 it in group.backgroundPermNames
662             }
663             reportRequestResult(affectedPermissions,
664                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
665             return STATE_SKIPPED
666         }
667 
668         if (isBackground && group.background.isGranted ||
669             !isBackground && group.foreground.isGranted) {
670             // If FINE location is not granted, do not grant it automatically when COARSE
671             // location is already granted.
672             if (group.permGroupName == LOCATION &&
673                     group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp
674                     == false) {
675                 return STATE_UNKNOWN
676             }
677 
678             if (group.permissions[perm]?.isGrantedIncludingAppOp == false) {
679                 if (isBackground) {
680                     KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
681                 } else {
682                     KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
683                 }
684                 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false,
685                     FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm))
686                 reportRequestResult(perm,
687                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
688             }
689 
690             return if (storedState == null) {
691                 STATE_SKIPPED
692             } else {
693                 STATE_ALLOWED
694             }
695         }
696         return STATE_UNKNOWN
697     }
698 
699     private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
700         val isBackground = perm in group.backgroundPermNames
701         var skipGroup = false
702         var state = STATE_UNKNOWN
703         when (permissionPolicy) {
704             DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT -> {
705                 if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
706                                 app, perm, user.identifier)) {
707                     if (isBackground) {
708                         KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
709                     } else {
710                         KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
711                     }
712                     KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
713                             FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
714                             filterPermissions = listOf(perm))
715                     state = STATE_ALLOWED
716                     skipGroup = true
717 
718                     getAutoGrantNotifier().onPermissionAutoGranted(perm)
719                     reportRequestResult(perm,
720                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
721                 }
722             }
723 
724             DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY -> {
725                 if (group.permissions[perm]?.isPolicyFixed == false) {
726                     KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
727                         FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
728                         filterPermissions = listOf(perm))
729                 }
730                 state = STATE_DENIED
731                 skipGroup = true
732 
733                 reportRequestResult(perm,
734                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED)
735             }
736         }
737         if (skipGroup && storedState == null) {
738             return STATE_SKIPPED
739         }
740         return state
741     }
742 
743     /**
744      * Upon the user clicking a button, grant permissions, if applicable.
745      *
746      * @param groupName The name of the permission group which was changed
747      * @param affectedForegroundPermissions The name of the foreground permission which was changed
748      * @param result The choice the user made regarding the group.
749      */
750     fun onPermissionGrantResult(
751         groupName: String?,
752         affectedForegroundPermissions: List<String>?,
753         result: Int
754     ) {
755         if (groupName == null) {
756             return
757         }
758         val foregroundGroupState = groupStates[groupName to false]
759         val backgroundGroupState = groupStates[groupName to true]
760         when (result) {
761             GrantPermissionsViewHandler.CANCELED -> {
762                 if (foregroundGroupState != null) {
763                     reportRequestResult(foregroundGroupState.affectedPermissions,
764                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
765                 }
766                 if (backgroundGroupState != null) {
767                     reportRequestResult(backgroundGroupState.affectedPermissions,
768                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
769                 }
770                 return
771             }
772             GRANTED_ALWAYS -> {
773                 if (foregroundGroupState != null) {
774                     onPermissionGrantResultSingleState(foregroundGroupState,
775                         affectedForegroundPermissions, granted = true, isOneTime = false,
776                         doNotAskAgain = false)
777                 }
778                 if (backgroundGroupState != null) {
779                     onPermissionGrantResultSingleState(backgroundGroupState,
780                         affectedForegroundPermissions, granted = true, isOneTime = false,
781                         doNotAskAgain = false)
782                 }
783             }
784             GRANTED_FOREGROUND_ONLY -> {
785                 if (foregroundGroupState != null) {
786                     onPermissionGrantResultSingleState(foregroundGroupState,
787                         affectedForegroundPermissions, granted = true, isOneTime = false,
788                         doNotAskAgain = false)
789                 }
790                 if (backgroundGroupState != null) {
791                     onPermissionGrantResultSingleState(backgroundGroupState,
792                         affectedForegroundPermissions, granted = false, isOneTime = false,
793                         doNotAskAgain = false)
794                 }
795             }
796             GrantPermissionsViewHandler.GRANTED_ONE_TIME -> {
797                 if (foregroundGroupState != null) {
798                     onPermissionGrantResultSingleState(foregroundGroupState,
799                         affectedForegroundPermissions, granted = true, isOneTime = true,
800                         doNotAskAgain = false)
801                 }
802                 if (backgroundGroupState != null) {
803                     onPermissionGrantResultSingleState(backgroundGroupState,
804                         affectedForegroundPermissions, granted = false, isOneTime = true,
805                         doNotAskAgain = false)
806                 }
807             }
808             DENIED -> {
809                 if (foregroundGroupState != null) {
810                     onPermissionGrantResultSingleState(foregroundGroupState,
811                         affectedForegroundPermissions, granted = false, isOneTime = false,
812                         doNotAskAgain = false)
813                 }
814                 if (backgroundGroupState != null) {
815                     onPermissionGrantResultSingleState(backgroundGroupState,
816                         affectedForegroundPermissions, granted = false, isOneTime = false,
817                         doNotAskAgain = false)
818                 }
819             }
820             DENIED_DO_NOT_ASK_AGAIN -> {
821                 if (foregroundGroupState != null) {
822                     onPermissionGrantResultSingleState(foregroundGroupState,
823                         affectedForegroundPermissions, granted = false, isOneTime = false,
824                         doNotAskAgain = true)
825                 }
826                 if (backgroundGroupState != null) {
827                     onPermissionGrantResultSingleState(backgroundGroupState,
828                         affectedForegroundPermissions, granted = false, isOneTime = false,
829                         doNotAskAgain = true)
830                 }
831             }
832         }
833     }
834 
835     private fun onPermissionGrantResultSingleState(
836         groupState: GroupState,
837         affectedForegroundPermissions: List<String>?,
838         granted: Boolean,
839         isOneTime: Boolean,
840         doNotAskAgain: Boolean
841     ) {
842         if (groupState.state != STATE_UNKNOWN) {
843             // We already dealt with this group, don't re-grant/re-revoke
844             return
845         }
846         val result: Int
847         if (granted) {
848             result = if (isOneTime) {
849                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
850             } else {
851                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
852             }
853             if (groupState.isBackground) {
854                 KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group,
855                     groupState.affectedPermissions)
856             } else {
857                 if (affectedForegroundPermissions == null) {
858                     KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
859                         groupState.affectedPermissions, isOneTime)
860                     // This prevents weird flag state when app targetSDK switches from S+ to R-
861                     if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
862                         KotlinUtils.setFlagsWhenLocationAccuracyChanged(
863                                 app, groupState.group, true)
864                     }
865                 } else {
866                     val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app,
867                             groupState.group, affectedForegroundPermissions, isOneTime)
868                     if (!isOneTime || newGroup.isOneTime) {
869                         KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
870                                 affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION))
871                     }
872                 }
873             }
874             groupState.state = STATE_ALLOWED
875         } else {
876             if (groupState.isBackground) {
877                 KotlinUtils.revokeBackgroundRuntimePermissions(app, groupState.group,
878                     userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions)
879             } else {
880                 if (affectedForegroundPermissions == null) {
881                     KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group,
882                         userFixed = doNotAskAgain,
883                         filterPermissions = groupState.affectedPermissions, oneTime = isOneTime)
884                 } else {
885                     KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group,
886                         userFixed = doNotAskAgain,
887                         filterPermissions = affectedForegroundPermissions, oneTime = isOneTime)
888                 }
889             }
890             result = if (doNotAskAgain) {
891                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
892             } else {
893                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
894             }
895             groupState.state = STATE_DENIED
896         }
897         reportRequestResult(groupState.affectedPermissions, result)
898         // group state has changed, reload liveData
899         requestInfosLiveData.update()
900     }
901 
902     private fun getGroupWithPerm(
903         perm: String,
904         groups: List<LightAppPermGroup>
905     ): LightAppPermGroup? {
906         val groupsWithPerm = groups.filter { perm in it.permissions }
907         if (groupsWithPerm.isEmpty()) {
908             return null
909         }
910         return groupsWithPerm.first()
911     }
912 
913     /**
914      * An internal class which represents the state of a current AppPermissionGroup grant request.
915      */
916     internal class GroupState(
917         internal val group: LightAppPermGroup,
918         internal val isBackground: Boolean,
919         internal val affectedPermissions: MutableList<String> = mutableListOf(),
920         internal var state: Int = STATE_UNKNOWN
921     ) {
922         override fun toString(): String {
923             val stateStr: String = when (state) {
924                 STATE_UNKNOWN -> "unknown"
925                 STATE_ALLOWED -> "granted"
926                 STATE_DENIED -> "denied"
927                 else -> "skipped"
928             }
929             return "${group.permGroupName} $isBackground $stateStr $affectedPermissions"
930         }
931     }
932 
933     private fun reportRequestResult(permissions: List<String>, result: Int) {
934         for (perm in permissions) {
935             reportRequestResult(perm, result)
936         }
937     }
938 
939     /**
940      * Report the result of a grant of a permission.
941      *
942      * @param permission The permission that was granted or denied
943      * @param result The permission grant result
944      */
945     private fun reportRequestResult(permission: String, result: Int) {
946         val isImplicit = permission !in requestedPermissions
947 
948         Log.v(LOG_TAG, "Permission grant result requestId=$sessionId " +
949             "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " +
950             "isImplicit=$isImplicit result=$result")
951 
952         PermissionControllerStatsLog.write(
953             PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, sessionId,
954             packageInfo.uid, packageName, permission, isImplicit, result)
955     }
956 
957     /**
958      * Save the group states of the view model, to allow for state restoration after lifecycle
959      * events
960      *
961      * @param outState The bundle in which to store state
962      */
963     fun saveInstanceState(outState: Bundle) {
964         for ((groupKey, groupState) in groupStates) {
965             val (groupName, isBackground) = groupKey
966             outState.putInt(getInstanceStateKey(groupName, isBackground), groupState.state)
967         }
968     }
969 
970     /**
971      * Determine if the activity should return permission state to the caller
972      *
973      * @return Whether or not state should be returned. False only if the package is pre-M, true
974      * otherwise.
975      */
976     fun shouldReturnPermissionState(): Boolean {
977         return if (packageInfoLiveData.value != null) {
978             packageInfoLiveData.value!!.targetSdkVersion >= Build.VERSION_CODES.M
979         } else {
980             // Should not be reached, as this method shouldn't be called before data is passed to
981             // the activity for the first time
982             try {
983                 Utils.getUserContext(app, user).packageManager
984                     .getApplicationInfo(packageName, 0).targetSdkVersion >= Build.VERSION_CODES.M
985             } catch (e: PackageManager.NameNotFoundException) {
986                 true
987             }
988         }
989     }
990 
991     /**
992      * Send the user directly to the AppPermissionFragment. Used for R+ apps.
993      *
994      * @param activity The current activity
995      * @param groupName The name of the permission group whose fragment should be opened
996      */
997     fun sendDirectlyToSettings(activity: Activity, groupName: String) {
998         if (activityResultCallback == null) {
999             startAppPermissionFragment(activity, groupName)
1000             activityResultCallback = Consumer { data ->
1001                 if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) {
1002                     // User didn't interact, count against rate limit
1003                     val group = groupStates[groupName to false]?.group
1004                         ?: groupStates[groupName to true]?.group ?: return@Consumer
1005                     if (group.background.isUserSet) {
1006                         KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_FIXED to true,
1007                             filterPermissions = group.backgroundPermNames)
1008                     } else {
1009                         KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to true,
1010                             filterPermissions = group.backgroundPermNames)
1011                     }
1012                 }
1013 
1014                 permGroupsToSkip.add(groupName)
1015                 // Update our liveData now that there is a new skipped group
1016                 requestInfosLiveData.update()
1017             }
1018         }
1019     }
1020 
1021     /**
1022      * Send the user to the AppPermissionFragment from a link. Used for Q- apps
1023      *
1024      * @param activity The current activity
1025      * @param groupName The name of the permission group whose fragment should be opened
1026      */
1027     fun sendToSettingsFromLink(activity: Activity, groupName: String) {
1028         startAppPermissionFragment(activity, groupName)
1029         activityResultCallback = Consumer { data ->
1030             val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
1031             if (returnGroupName != null) {
1032                 permGroupsToSkip.add(returnGroupName)
1033                 val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1)
1034                 logSettingsInteraction(returnGroupName, result)
1035                 requestInfosLiveData.update()
1036             }
1037         }
1038     }
1039 
1040     private fun startAppPermissionFragment(activity: Activity, groupName: String) {
1041         val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
1042             .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
1043             .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
1044             .putExtra(Intent.EXTRA_USER, user)
1045             .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME,
1046                 GrantPermissionsActivity::class.java.name)
1047             .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
1048             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
1049         activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
1050     }
1051 
1052     private fun getInstanceStateKey(groupName: String, isBackground: Boolean): String {
1053         return "${this::class.java.name}_${groupName}_$isBackground"
1054     }
1055 
1056     private fun logSettingsInteraction(groupName: String, result: Int) {
1057         val foregroundGroupState = groupStates[groupName to false]
1058         val backgroundGroupState = groupStates[groupName to true]
1059         val deniedPrejudiceInSettings =
1060             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS
1061         when (result) {
1062             GRANTED_ALWAYS -> {
1063                 if (foregroundGroupState != null) {
1064                     reportRequestResult(foregroundGroupState.affectedPermissions,
1065                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1066                 }
1067                 if (backgroundGroupState != null) {
1068                     reportRequestResult(backgroundGroupState.affectedPermissions,
1069                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1070                 }
1071             }
1072             GRANTED_FOREGROUND_ONLY -> {
1073                 if (foregroundGroupState != null) {
1074                     reportRequestResult(foregroundGroupState.affectedPermissions,
1075                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1076                 }
1077                 if (backgroundGroupState != null) {
1078                     reportRequestResult(backgroundGroupState.affectedPermissions,
1079                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1080                 }
1081             }
1082             DENIED -> {
1083                 if (foregroundGroupState != null) {
1084                     reportRequestResult(foregroundGroupState.affectedPermissions,
1085                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1086                 }
1087                 if (backgroundGroupState != null) {
1088                     reportRequestResult(backgroundGroupState.affectedPermissions,
1089                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1090                 }
1091             }
1092             DENIED_DO_NOT_ASK_AGAIN -> {
1093                 if (foregroundGroupState != null) {
1094                     reportRequestResult(foregroundGroupState.affectedPermissions,
1095                         deniedPrejudiceInSettings)
1096                 }
1097                 if (backgroundGroupState != null) {
1098                     reportRequestResult(backgroundGroupState.affectedPermissions,
1099                         deniedPrejudiceInSettings)
1100                 }
1101             }
1102         }
1103     }
1104 
1105     /**
1106      * Log all permission groups which were requested
1107      */
1108     fun logRequestedPermissionGroups() {
1109         if (groupStates.isEmpty()) {
1110             return
1111         }
1112         val groups = groupStates.map { it.value.group }
1113         SafetyNetLogger.logPermissionsRequested(packageName, packageInfo.uid, groups)
1114     }
1115 
1116     /**
1117      * Log information about the buttons which were shown and clicked by the user.
1118      *
1119      * @param groupName The name of the permission group which was interacted with
1120      * @param selectedPrecision Selected precision of the location permission - bit flags indicate
1121      *                          which locations were chosen
1122      * @param clickedButton The button that was clicked by the user
1123      * @param presentedButtons All buttons which were shown to the user
1124      */
1125     fun logClickedButtons(
1126         groupName: String?,
1127         selectedPrecision: Int,
1128         clickedButton: Int,
1129         presentedButtons: Int
1130     ) {
1131         if (groupName == null) {
1132             return
1133         }
1134         var selectedLocations = 0
1135         // log permissions if it's 1) first time requesting both locations OR 2) upgrade flow
1136         if (isFirstTimeRequestingFineAndCoarse ||
1137                 selectedPrecision ==
1138                     1 shl PERMISSION_TO_BIT_SHIFT[ACCESS_FINE_LOCATION]!!) {
1139             selectedLocations = selectedPrecision
1140         }
1141         PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
1142                 groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId,
1143                 packageInfo.targetSdkVersion, selectedLocations)
1144         Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" +
1145                 "$groupName uid=${packageInfo.uid} selectedLocations=$selectedLocations " +
1146                 "package=$packageName presentedButtons=$presentedButtons " +
1147                 "clickedButton=$clickedButton sessionId=$sessionId " +
1148                 "targetSdk=${packageInfo.targetSdkVersion}")
1149     }
1150 
1151     /**
1152      * Use the autoGrantNotifier to notify of auto-granted permissions.
1153      */
1154     fun autoGrantNotify() {
1155         autoGrantNotifier?.notifyOfAutoGrantPermissions(true)
1156     }
1157 
1158     companion object {
1159         private const val APP_PERMISSION_REQUEST_CODE = 1
1160         private const val STATE_UNKNOWN = 0
1161         private const val STATE_ALLOWED = 1
1162         private const val STATE_DENIED = 2
1163         private const val STATE_SKIPPED = 3
1164         private const val STATE_ALREADY_ALLOWED = 4
1165 
1166         /**
1167          * An enum that represents the type of message which should be shown- foreground,
1168          * background, upgrade, or no message.
1169          */
1170         enum class RequestMessage(request: Int) {
1171             FG_MESSAGE(0),
1172             BG_MESSAGE(1),
1173             UPGRADE_MESSAGE(2),
1174             NO_MESSAGE(3),
1175             FG_FINE_LOCATION_MESSAGE(4),
1176             FG_COARSE_LOCATION_MESSAGE(5)
1177         }
1178     }
1179 }
1180 
1181 /**
1182  * Factory for an AppPermissionViewModel
1183  *
1184  * @param app The current application
1185  * @param packageName The name of the package this ViewModel represents
1186  */
1187 class GrantPermissionsViewModelFactory(
1188     private val app: Application,
1189     private val packageName: String,
1190     private val requestedPermissions: Array<String>,
1191     private val sessionId: Long,
1192     private val savedState: Bundle?
1193 ) : ViewModelProvider.Factory {
1194     override fun <T : ViewModel> create(modelClass: Class<T>): T {
1195         @Suppress("UNCHECKED_CAST")
1196         return GrantPermissionsViewModel(app, packageName, requestedPermissions.toList(), sessionId,
1197             savedState) as T
1198     }
1199 }
1200