• 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.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                     if (needFgPermissions) {
422                         locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
423                         if (fgState != null &&
424                                 fgState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
425                             val coarseLocationPerm =
426                                 groupState.group.allPermissions[ACCESS_COARSE_LOCATION]
427                             if (coarseLocationPerm?.isGrantedIncludingAppOp == true) {
428                                 // Upgrade flow
429                                 locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY] = true
430                                 message = RequestMessage.FG_FINE_LOCATION_MESSAGE
431                                 // If COARSE was granted one time, hide 'While in use' button
432                                 if (coarseLocationPerm.isOneTime) {
433                                     buttonVisibilities[ALLOW_FOREGROUND_BUTTON] = false
434                                 }
435                             } else {
436                                 if (coarseLocationPerm?.isOneTime == false &&
437                                         !coarseLocationPerm.isUserSet &&
438                                         !coarseLocationPerm.isUserFixed) {
439                                     isFirstTimeRequestingFineAndCoarse = true
440                                 }
441                                 // Normal flow with both Coarse and Fine locations
442                                 locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true
443                                 // Steps to decide location accuracy default state
444                                 // 1. If none of the FINE and COARSE isSelectedLocationAccuracy
445                                 //    flags is set, then use default precision from device config.
446                                 // 2. Otherwise set to whichever isSelectedLocationAccuracy is true.
447                                 val fineLocationPerm =
448                                         groupState.group.allPermissions[ACCESS_FINE_LOCATION]
449                                 if (coarseLocationPerm?.isSelectedLocationAccuracy == false &&
450                                         fineLocationPerm?.isSelectedLocationAccuracy == false) {
451                                     if (getDefaultPrecision()) {
452                                         locationVisibilities[FINE_RADIO_BUTTON] = true
453                                     } else {
454                                         locationVisibilities[COARSE_RADIO_BUTTON] = true
455                                     }
456                                 } else if (coarseLocationPerm?.isSelectedLocationAccuracy == true) {
457                                     locationVisibilities[COARSE_RADIO_BUTTON] = true
458                                 } else {
459                                     locationVisibilities[FINE_RADIO_BUTTON] = true
460                                 }
461                             }
462                         } else if (fgState != null && fgState.affectedPermissions
463                                         .contains(ACCESS_COARSE_LOCATION)) {
464                             // Request Coarse only
465                             locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY] = true
466                             message = RequestMessage.FG_COARSE_LOCATION_MESSAGE
467                         }
468                     }
469                 }
470 
471                 requestInfos.add(RequestInfo(
472                     groupInfo,
473                     buttonVisibilities,
474                     locationVisibilities,
475                     message,
476                     detailMessage))
477             }
478             requestInfos.sortWith(Comparator { rhs, lhs ->
479                 val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
480                 val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
481                 if (rhsHasOneTime && !lhsHasOneTime) {
482                     -1
483                 } else if (!rhsHasOneTime && lhsHasOneTime) {
484                     1
485                 } else {
486                     rhs.groupName.compareTo(lhs.groupName)
487                 }
488             })
489 
490             value = if (requestInfos.any { it.sendToSettingsImmediately } &&
491                 requestInfos.size > 1) {
492                 Log.e(LOG_TAG, "For R+ apps, background permissions must be requested " +
493                     "individually")
494                 null
495             } else {
496                 requestInfos
497             }
498         }
499     }
500 
501     /**
502      * Converts a list of LightAppPermGroups into a list of GroupStates
503      */
504     private fun getRequiredGroupStates(
505         groups: List<LightAppPermGroup>
506     ): MutableMap<Pair<String, Boolean>, GroupState> {
507         val groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
508         val filteredPermissions = unfilteredAffectedPermissions.filter { perm ->
509             val group = getGroupWithPerm(perm, groups)
510             group != null && isPermissionGrantableAndNotFixed(perm, group)
511         }
512         for (perm in filteredPermissions) {
513             val group = getGroupWithPerm(perm, groups)!!
514 
515             val isBackground = perm in group.backgroundPermNames
516             val groupStateInfo = groupStates.getOrPut(group.permGroupName to isBackground) {
517                 GroupState(group, isBackground)
518             }
519 
520             var currGroupState = groupStateInfo.state
521             if (storedState != null && currGroupState != STATE_UNKNOWN) {
522                 currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName,
523                     isBackground), STATE_UNKNOWN)
524             }
525 
526             val otherGroupPermissions = filteredPermissions.filter { it in group.permissions }
527             val groupStateOfPerm = getGroupState(perm, group, otherGroupPermissions)
528             if (groupStateOfPerm != STATE_UNKNOWN) {
529                 currGroupState = groupStateOfPerm
530             }
531 
532             if (group.permGroupName in permGroupsToSkip) {
533                 currGroupState = STATE_SKIPPED
534             }
535 
536             if (currGroupState != STATE_UNKNOWN) {
537                 groupStateInfo.state = currGroupState
538             }
539             // If we saved state, load it
540             groupStateInfo.affectedPermissions.add(perm)
541         }
542         return groupStates
543     }
544 
545     /**
546      * Get the actually requested permissions when a permission is requested.
547      *
548      * >In some cases requesting to grant a single permission requires the system to grant
549      * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
550      * group to be granted. Another case are permissions that are split into two. For apps that
551      * target an SDK before the split, this method automatically adds the split off permission.
552      *
553      * @param perm The requested permission
554      *
555      * @return The actually requested permissions
556      */
557     private fun computeAffectedPermissions(
558         perm: String,
559         appPermissions: Map<String, List<String>>
560     ): List<String> {
561         val requestingAppTargetSDK = packageInfo.targetSdkVersion
562 
563         // If a permission is split, all permissions the original permission is split into are
564         // affected
565         val extendedBySplitPerms = mutableListOf(perm)
566 
567         val splitPerms = app.getSystemService(PermissionManager::class.java)!!.splitPermissions
568         for (splitPerm in splitPerms) {
569 
570             if (requestingAppTargetSDK < splitPerm.targetSdk && perm == splitPerm.splitPermission) {
571                 extendedBySplitPerms.addAll(splitPerm.newPermissions)
572             }
573         }
574 
575         // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
576         if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
577             val extendedBySplitPermsAndGroup = mutableListOf<String>()
578 
579             for (splitPerm in extendedBySplitPerms) {
580                 val groups = appPermissions.filter { splitPerm in it.value }
581                 if (groups.isEmpty()) {
582                     continue
583                 }
584 
585                 val permissionsInGroup = groups.values.first()
586                 for (permissionInGroup in permissionsInGroup) {
587                     extendedBySplitPermsAndGroup.add(permissionInGroup)
588                 }
589             }
590 
591             return extendedBySplitPermsAndGroup
592         } else {
593             return extendedBySplitPerms
594         }
595     }
596 
597     private fun isPermissionGrantableAndNotFixed(perm: String, group: LightAppPermGroup): Boolean {
598 
599         // If the permission is restricted it does not show in the UI and
600         // is not added to the group at all, so check that first.
601         if (perm in group.packageInfo.requestedPermissions && perm !in group.permissions) {
602             reportRequestResult(perm,
603                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION)
604             return false
605         }
606 
607         val subGroup = if (perm in group.backgroundPermNames) {
608             group.background
609         } else {
610             group.foreground
611         }
612 
613         val lightPermission = group.permissions[perm] ?: return false
614 
615         if (!subGroup.isGrantable) {
616             reportRequestResult(perm, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
617             // Skip showing groups that we know cannot be granted.
618             return false
619         } else if (subGroup.isUserFixed) {
620             reportRequestResult(perm,
621                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED)
622             return false
623         } else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) {
624             reportRequestResult(perm,
625                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED)
626             return false
627         }
628 
629         return true
630     }
631 
632     private fun getGroupState(
633         perm: String,
634         group: LightAppPermGroup,
635         groupRequestedPermissions: List<String>
636     ): Int {
637         val policyState = getStateFromPolicy(perm, group)
638         if (policyState != STATE_UNKNOWN) {
639             return policyState
640         }
641 
642         val isBackground = perm in group.backgroundPermNames
643 
644         val hasForegroundRequest = groupRequestedPermissions.any {
645             it !in group.backgroundPermNames
646         }
647 
648         // Do not attempt to grant background access if foreground access is not either already
649         // granted or requested
650         if (isBackground && !group.foreground.isGranted && !hasForegroundRequest) {
651             Log.w(LOG_TAG, "Cannot grant $perm as the matching foreground permission is not " +
652                 "already granted.")
653             val affectedPermissions = groupRequestedPermissions.filter {
654                 it in group.backgroundPermNames
655             }
656             reportRequestResult(affectedPermissions,
657                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
658             return STATE_SKIPPED
659         }
660 
661         if (isBackground && group.background.isGranted ||
662             !isBackground && group.foreground.isGranted) {
663             // If FINE location is not granted, do not grant it automatically when COARSE
664             // location is already granted.
665             if (group.permGroupName == LOCATION &&
666                     group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp
667                     == false) {
668                 return STATE_UNKNOWN
669             }
670 
671             if (group.permissions[perm]?.isGrantedIncludingAppOp == false) {
672                 if (isBackground) {
673                     KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
674                 } else {
675                     KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
676                 }
677                 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false,
678                     FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm))
679                 reportRequestResult(perm,
680                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
681             }
682 
683             return if (storedState == null) {
684                 STATE_SKIPPED
685             } else {
686                 STATE_ALLOWED
687             }
688         }
689         return STATE_UNKNOWN
690     }
691 
692     private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
693         val isBackground = perm in group.backgroundPermNames
694         var skipGroup = false
695         var state = STATE_UNKNOWN
696         when (permissionPolicy) {
697             DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT -> {
698                 if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
699                                 app, perm, user.identifier)) {
700                     if (isBackground) {
701                         KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
702                     } else {
703                         KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
704                     }
705                     KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
706                             FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
707                             filterPermissions = listOf(perm))
708                     state = STATE_ALLOWED
709                     skipGroup = true
710 
711                     getAutoGrantNotifier().onPermissionAutoGranted(perm)
712                     reportRequestResult(perm,
713                             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
714                 }
715             }
716 
717             DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY -> {
718                 if (group.permissions[perm]?.isPolicyFixed == false) {
719                     KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
720                         FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
721                         filterPermissions = listOf(perm))
722                 }
723                 state = STATE_DENIED
724                 skipGroup = true
725 
726                 reportRequestResult(perm,
727                     PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_DENIED)
728             }
729         }
730         if (skipGroup && storedState == null) {
731             return STATE_SKIPPED
732         }
733         return state
734     }
735 
736     /**
737      * Upon the user clicking a button, grant permissions, if applicable.
738      *
739      * @param groupName The name of the permission group which was changed
740      * @param affectedForegroundPermissions The name of the foreground permission which was changed
741      * @param result The choice the user made regarding the group.
742      */
743     fun onPermissionGrantResult(
744         groupName: String?,
745         affectedForegroundPermissions: List<String>?,
746         result: Int
747     ) {
748         if (groupName == null) {
749             return
750         }
751         val foregroundGroupState = groupStates[groupName to false]
752         val backgroundGroupState = groupStates[groupName to true]
753         when (result) {
754             GrantPermissionsViewHandler.CANCELED -> {
755                 if (foregroundGroupState != null) {
756                     reportRequestResult(foregroundGroupState.affectedPermissions,
757                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
758                 }
759                 if (backgroundGroupState != null) {
760                     reportRequestResult(backgroundGroupState.affectedPermissions,
761                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
762                 }
763                 return
764             }
765             GRANTED_ALWAYS -> {
766                 if (foregroundGroupState != null) {
767                     onPermissionGrantResultSingleState(foregroundGroupState,
768                         affectedForegroundPermissions, granted = true, isOneTime = false,
769                         doNotAskAgain = false)
770                 }
771                 if (backgroundGroupState != null) {
772                     onPermissionGrantResultSingleState(backgroundGroupState,
773                         affectedForegroundPermissions, granted = true, isOneTime = false,
774                         doNotAskAgain = false)
775                 }
776             }
777             GRANTED_FOREGROUND_ONLY -> {
778                 if (foregroundGroupState != null) {
779                     onPermissionGrantResultSingleState(foregroundGroupState,
780                         affectedForegroundPermissions, granted = true, isOneTime = false,
781                         doNotAskAgain = false)
782                 }
783                 if (backgroundGroupState != null) {
784                     onPermissionGrantResultSingleState(backgroundGroupState,
785                         affectedForegroundPermissions, granted = false, isOneTime = false,
786                         doNotAskAgain = false)
787                 }
788             }
789             GrantPermissionsViewHandler.GRANTED_ONE_TIME -> {
790                 if (foregroundGroupState != null) {
791                     onPermissionGrantResultSingleState(foregroundGroupState,
792                         affectedForegroundPermissions, granted = true, isOneTime = true,
793                         doNotAskAgain = false)
794                 }
795                 if (backgroundGroupState != null) {
796                     onPermissionGrantResultSingleState(backgroundGroupState,
797                         affectedForegroundPermissions, granted = false, isOneTime = true,
798                         doNotAskAgain = false)
799                 }
800             }
801             DENIED -> {
802                 if (foregroundGroupState != null) {
803                     onPermissionGrantResultSingleState(foregroundGroupState,
804                         affectedForegroundPermissions, granted = false, isOneTime = false,
805                         doNotAskAgain = false)
806                 }
807                 if (backgroundGroupState != null) {
808                     onPermissionGrantResultSingleState(backgroundGroupState,
809                         affectedForegroundPermissions, granted = false, isOneTime = false,
810                         doNotAskAgain = false)
811                 }
812             }
813             DENIED_DO_NOT_ASK_AGAIN -> {
814                 if (foregroundGroupState != null) {
815                     onPermissionGrantResultSingleState(foregroundGroupState,
816                         affectedForegroundPermissions, granted = false, isOneTime = false,
817                         doNotAskAgain = true)
818                 }
819                 if (backgroundGroupState != null) {
820                     onPermissionGrantResultSingleState(backgroundGroupState,
821                         affectedForegroundPermissions, granted = false, isOneTime = false,
822                         doNotAskAgain = true)
823                 }
824             }
825         }
826     }
827 
828     private fun onPermissionGrantResultSingleState(
829         groupState: GroupState,
830         affectedForegroundPermissions: List<String>?,
831         granted: Boolean,
832         isOneTime: Boolean,
833         doNotAskAgain: Boolean
834     ) {
835         if (groupState.state != STATE_UNKNOWN) {
836             // We already dealt with this group, don't re-grant/re-revoke
837             return
838         }
839         val result: Int
840         if (granted) {
841             result = if (isOneTime) {
842                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
843             } else {
844                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
845             }
846             if (groupState.isBackground) {
847                 KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group,
848                     groupState.affectedPermissions)
849             } else {
850                 if (affectedForegroundPermissions == null) {
851                     KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
852                         groupState.affectedPermissions, isOneTime)
853                     // This prevents weird flag state when app targetSDK switches from S+ to R-
854                     if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
855                         KotlinUtils.setFlagsWhenLocationAccuracyChanged(
856                                 app, groupState.group, true)
857                     }
858                 } else {
859                     val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app,
860                             groupState.group, affectedForegroundPermissions, isOneTime)
861                     if (!isOneTime || newGroup.isOneTime) {
862                         KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
863                                 affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION))
864                     }
865                 }
866             }
867             groupState.state = STATE_ALLOWED
868         } else {
869             if (groupState.isBackground) {
870                 KotlinUtils.revokeBackgroundRuntimePermissions(app, groupState.group,
871                     userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions)
872             } else {
873                 if (affectedForegroundPermissions == null) {
874                     KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group,
875                         userFixed = doNotAskAgain,
876                         filterPermissions = groupState.affectedPermissions, oneTime = isOneTime)
877                 } else {
878                     KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group,
879                         userFixed = doNotAskAgain,
880                         filterPermissions = affectedForegroundPermissions, oneTime = isOneTime)
881                 }
882             }
883             result = if (doNotAskAgain) {
884                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
885             } else {
886                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
887             }
888             groupState.state = STATE_DENIED
889         }
890         reportRequestResult(groupState.affectedPermissions, result)
891         // group state has changed, reload liveData
892         requestInfosLiveData.update()
893     }
894 
895     private fun getGroupWithPerm(
896         perm: String,
897         groups: List<LightAppPermGroup>
898     ): LightAppPermGroup? {
899         val groupsWithPerm = groups.filter { perm in it.permissions }
900         if (groupsWithPerm.isEmpty()) {
901             return null
902         }
903         return groupsWithPerm.first()
904     }
905 
906     /**
907      * An internal class which represents the state of a current AppPermissionGroup grant request.
908      */
909     internal class GroupState(
910         internal val group: LightAppPermGroup,
911         internal val isBackground: Boolean,
912         internal val affectedPermissions: MutableList<String> = mutableListOf(),
913         internal var state: Int = STATE_UNKNOWN
914     ) {
915         override fun toString(): String {
916             val stateStr: String = when (state) {
917                 STATE_UNKNOWN -> "unknown"
918                 STATE_ALLOWED -> "granted"
919                 STATE_DENIED -> "denied"
920                 else -> "skipped"
921             }
922             return "${group.permGroupName} $isBackground $stateStr $affectedPermissions"
923         }
924     }
925 
926     private fun reportRequestResult(permissions: List<String>, result: Int) {
927         for (perm in permissions) {
928             reportRequestResult(perm, result)
929         }
930     }
931 
932     /**
933      * Report the result of a grant of a permission.
934      *
935      * @param permission The permission that was granted or denied
936      * @param result The permission grant result
937      */
938     private fun reportRequestResult(permission: String, result: Int) {
939         val isImplicit = permission !in requestedPermissions
940 
941         Log.v(LOG_TAG, "Permission grant result requestId=$sessionId " +
942             "callingUid=${packageInfo.uid} callingPackage=$packageName permission=$permission " +
943             "isImplicit=$isImplicit result=$result")
944 
945         PermissionControllerStatsLog.write(
946             PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, sessionId,
947             packageInfo.uid, packageName, permission, isImplicit, result)
948     }
949 
950     /**
951      * Save the group states of the view model, to allow for state restoration after lifecycle
952      * events
953      *
954      * @param outState The bundle in which to store state
955      */
956     fun saveInstanceState(outState: Bundle) {
957         for ((groupKey, groupState) in groupStates) {
958             val (groupName, isBackground) = groupKey
959             outState.putInt(getInstanceStateKey(groupName, isBackground), groupState.state)
960         }
961     }
962 
963     /**
964      * Determine if the activity should return permission state to the caller
965      *
966      * @return Whether or not state should be returned. False only if the package is pre-M, true
967      * otherwise.
968      */
969     fun shouldReturnPermissionState(): Boolean {
970         return if (packageInfoLiveData.value != null) {
971             packageInfoLiveData.value!!.targetSdkVersion >= Build.VERSION_CODES.M
972         } else {
973             // Should not be reached, as this method shouldn't be called before data is passed to
974             // the activity for the first time
975             try {
976                 Utils.getUserContext(app, user).packageManager
977                     .getApplicationInfo(packageName, 0).targetSdkVersion >= Build.VERSION_CODES.M
978             } catch (e: PackageManager.NameNotFoundException) {
979                 true
980             }
981         }
982     }
983 
984     /**
985      * Send the user directly to the AppPermissionFragment. Used for R+ apps.
986      *
987      * @param activity The current activity
988      * @param groupName The name of the permission group whose fragment should be opened
989      */
990     fun sendDirectlyToSettings(activity: Activity, groupName: String) {
991         if (activityResultCallback == null) {
992             startAppPermissionFragment(activity, groupName)
993             activityResultCallback = Consumer { data ->
994                 if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) {
995                     // User didn't interact, count against rate limit
996                     val group = groupStates[groupName to false]?.group
997                         ?: groupStates[groupName to true]?.group ?: return@Consumer
998                     if (group.background.isUserSet) {
999                         KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_FIXED to true,
1000                             filterPermissions = group.backgroundPermNames)
1001                     } else {
1002                         KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to true,
1003                             filterPermissions = group.backgroundPermNames)
1004                     }
1005                 }
1006 
1007                 permGroupsToSkip.add(groupName)
1008                 // Update our liveData now that there is a new skipped group
1009                 requestInfosLiveData.update()
1010             }
1011         }
1012     }
1013 
1014     /**
1015      * Send the user to the AppPermissionFragment from a link. Used for Q- apps
1016      *
1017      * @param activity The current activity
1018      * @param groupName The name of the permission group whose fragment should be opened
1019      */
1020     fun sendToSettingsFromLink(activity: Activity, groupName: String) {
1021         startAppPermissionFragment(activity, groupName)
1022         activityResultCallback = Consumer { data ->
1023             val returnGroupName = data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED)
1024             if (returnGroupName != null) {
1025                 permGroupsToSkip.add(returnGroupName)
1026                 val result = data.getIntExtra(EXTRA_RESULT_PERMISSION_RESULT, -1)
1027                 logSettingsInteraction(returnGroupName, result)
1028                 requestInfosLiveData.update()
1029             }
1030         }
1031     }
1032 
1033     private fun startAppPermissionFragment(activity: Activity, groupName: String) {
1034         val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
1035             .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
1036             .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
1037             .putExtra(Intent.EXTRA_USER, user)
1038             .putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME,
1039                 GrantPermissionsActivity::class.java.name)
1040             .putExtra(Constants.EXTRA_SESSION_ID, sessionId)
1041             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
1042         activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
1043     }
1044 
1045     private fun getInstanceStateKey(groupName: String, isBackground: Boolean): String {
1046         return "${this::class.java.name}_${groupName}_$isBackground"
1047     }
1048 
1049     private fun logSettingsInteraction(groupName: String, result: Int) {
1050         val foregroundGroupState = groupStates[groupName to false]
1051         val backgroundGroupState = groupStates[groupName to true]
1052         val deniedPrejudiceInSettings =
1053             PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS
1054         when (result) {
1055             GRANTED_ALWAYS -> {
1056                 if (foregroundGroupState != null) {
1057                     reportRequestResult(foregroundGroupState.affectedPermissions,
1058                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1059                 }
1060                 if (backgroundGroupState != null) {
1061                     reportRequestResult(backgroundGroupState.affectedPermissions,
1062                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1063                 }
1064             }
1065             GRANTED_FOREGROUND_ONLY -> {
1066                 if (foregroundGroupState != null) {
1067                     reportRequestResult(foregroundGroupState.affectedPermissions,
1068                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS)
1069                 }
1070                 if (backgroundGroupState != null) {
1071                     reportRequestResult(backgroundGroupState.affectedPermissions,
1072                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1073                 }
1074             }
1075             DENIED -> {
1076                 if (foregroundGroupState != null) {
1077                     reportRequestResult(foregroundGroupState.affectedPermissions,
1078                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1079                 }
1080                 if (backgroundGroupState != null) {
1081                     reportRequestResult(backgroundGroupState.affectedPermissions,
1082                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
1083                 }
1084             }
1085             DENIED_DO_NOT_ASK_AGAIN -> {
1086                 if (foregroundGroupState != null) {
1087                     reportRequestResult(foregroundGroupState.affectedPermissions,
1088                         deniedPrejudiceInSettings)
1089                 }
1090                 if (backgroundGroupState != null) {
1091                     reportRequestResult(backgroundGroupState.affectedPermissions,
1092                         deniedPrejudiceInSettings)
1093                 }
1094             }
1095         }
1096     }
1097 
1098     /**
1099      * Log all permission groups which were requested
1100      */
1101     fun logRequestedPermissionGroups() {
1102         if (groupStates.isEmpty()) {
1103             return
1104         }
1105         val groups = groupStates.map { it.value.group }
1106         SafetyNetLogger.logPermissionsRequested(packageName, packageInfo.uid, groups)
1107     }
1108 
1109     /**
1110      * Log information about the buttons which were shown and clicked by the user.
1111      *
1112      * @param groupName The name of the permission group which was interacted with
1113      * @param selectedPrecision Selected precision of the location permission - bit flags indicate
1114      *                          which locations were chosen
1115      * @param clickedButton The button that was clicked by the user
1116      * @param presentedButtons All buttons which were shown to the user
1117      */
1118     fun logClickedButtons(
1119         groupName: String?,
1120         selectedPrecision: Int,
1121         clickedButton: Int,
1122         presentedButtons: Int
1123     ) {
1124         if (groupName == null) {
1125             return
1126         }
1127         var selectedLocations = 0
1128         // log permissions if it's 1) first time requesting both locations OR 2) upgrade flow
1129         if (isFirstTimeRequestingFineAndCoarse ||
1130                 selectedPrecision ==
1131                     1 shl PERMISSION_TO_BIT_SHIFT[ACCESS_FINE_LOCATION]!!) {
1132             selectedLocations = selectedPrecision
1133         }
1134         PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
1135                 groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId,
1136                 packageInfo.targetSdkVersion, selectedLocations)
1137         Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" +
1138                 "$groupName uid=${packageInfo.uid} selectedLocations=$selectedLocations " +
1139                 "package=$packageName presentedButtons=$presentedButtons " +
1140                 "clickedButton=$clickedButton sessionId=$sessionId " +
1141                 "targetSdk=${packageInfo.targetSdkVersion}")
1142     }
1143 
1144     /**
1145      * Use the autoGrantNotifier to notify of auto-granted permissions.
1146      */
1147     fun autoGrantNotify() {
1148         autoGrantNotifier?.notifyOfAutoGrantPermissions(true)
1149     }
1150 
1151     companion object {
1152         private const val APP_PERMISSION_REQUEST_CODE = 1
1153         private const val STATE_UNKNOWN = 0
1154         private const val STATE_ALLOWED = 1
1155         private const val STATE_DENIED = 2
1156         private const val STATE_SKIPPED = 3
1157         private const val STATE_ALREADY_ALLOWED = 4
1158 
1159         /**
1160          * An enum that represents the type of message which should be shown- foreground,
1161          * background, upgrade, or no message.
1162          */
1163         enum class RequestMessage(request: Int) {
1164             FG_MESSAGE(0),
1165             BG_MESSAGE(1),
1166             UPGRADE_MESSAGE(2),
1167             NO_MESSAGE(3),
1168             FG_FINE_LOCATION_MESSAGE(4),
1169             FG_COARSE_LOCATION_MESSAGE(5)
1170         }
1171     }
1172 }
1173 
1174 /**
1175  * Factory for an AppPermissionViewModel
1176  *
1177  * @param app The current application
1178  * @param packageName The name of the package this ViewModel represents
1179  */
1180 class GrantPermissionsViewModelFactory(
1181     private val app: Application,
1182     private val packageName: String,
1183     private val requestedPermissions: Array<String>,
1184     private val sessionId: Long,
1185     private val savedState: Bundle?
1186 ) : ViewModelProvider.Factory {
createnull1187     override fun <T : ViewModel> create(modelClass: Class<T>): T {
1188         @Suppress("UNCHECKED_CAST")
1189         return GrantPermissionsViewModel(app, packageName, requestedPermissions.toList(), sessionId,
1190             savedState) as T
1191     }
1192 }
1193