1 /*
2  * Copyright (C) 2023 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.grantPermissions
18 
19 import android.Manifest.permission.ACCESS_COARSE_LOCATION
20 import android.Manifest.permission.ACCESS_FINE_LOCATION
21 import android.os.Build
22 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
23 import com.android.permissioncontroller.permission.ui.model.DenyButton
24 import com.android.permissioncontroller.permission.ui.model.Prompt
25 import com.android.permissioncontroller.permission.utils.KotlinUtils
26 
27 /**
28  * The Location Grant behavior is the same as the Background group behavior, up until S. After S,
29  * the fine and coarse location permissions were allowed to be granted separately, and this created
30  * a new set of grant dialogs.
31  */
32 object LocationGrantBehavior : GrantBehavior() {
getPromptnull33     override fun getPrompt(
34         group: LightAppPermGroup,
35         requestedPerms: Set<String>,
36         isSystemTriggeredPrompt: Boolean
37     ): Prompt {
38         val backgroundPrompt = BackgroundGrantBehavior.getPrompt(group, requestedPerms)
39         val requestsBackground = requestedPerms.any { it in group.backgroundPermNames }
40         val coarseGranted =
41             group.permissions[ACCESS_COARSE_LOCATION]?.isGrantedIncludingAppOp == true
42         return if (!supportsLocationAccuracy(group) || requestsBackground) {
43             backgroundPrompt
44         } else if (requestedPerms.contains(ACCESS_FINE_LOCATION)) {
45             if (coarseGranted) {
46                 Prompt.LOCATION_FINE_UPGRADE
47             } else if (isFineLocationHighlighted(group)) {
48                 Prompt.LOCATION_TWO_BUTTON_FINE_HIGHLIGHT
49             } else {
50                 Prompt.LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT
51             }
52         } else if (requestedPerms.contains(ACCESS_COARSE_LOCATION) && !coarseGranted) {
53             Prompt.LOCATION_COARSE_ONLY
54         } else {
55             backgroundPrompt
56         }
57     }
58 
getDenyButtonnull59     override fun getDenyButton(
60         group: LightAppPermGroup,
61         requestedPerms: Set<String>,
62         prompt: Prompt
63     ): DenyButton {
64         return BackgroundGrantBehavior.getDenyButton(group, requestedPerms, prompt)
65     }
66 
isGroupFullyGrantednull67     override fun isGroupFullyGranted(
68         group: LightAppPermGroup,
69         requestedPerms: Set<String>
70     ): Boolean {
71         val requestsBackground = requestedPerms.any { it in group.backgroundPermNames }
72         if (!supportsLocationAccuracy(group) || requestsBackground) {
73             return BackgroundGrantBehavior.isGroupFullyGranted(group, requestedPerms)
74         }
75         return isForegroundFullyGranted(group, requestedPerms)
76     }
77 
isForegroundFullyGrantednull78     override fun isForegroundFullyGranted(
79         group: LightAppPermGroup,
80         requestedPerms: Set<String>
81     ): Boolean {
82         if (!supportsLocationAccuracy(group)) {
83             return BackgroundGrantBehavior.isForegroundFullyGranted(group, requestedPerms)
84         }
85 
86         if (requestedPerms.contains(ACCESS_FINE_LOCATION)) {
87             return group.permissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == true
88         }
89 
90         return group.foreground.isGrantedExcludingRWROrAllRWR
91     }
92 
isPermissionFixednull93     override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
94         if (!supportsLocationAccuracy(group) || perm != ACCESS_COARSE_LOCATION) {
95             return BackgroundGrantBehavior.isPermissionFixed(group, perm)
96         }
97 
98         // If the location group is user fixed but ACCESS_COARSE_LOCATION is not, then
99         // ACCESS_FINE_LOCATION must be user fixed. In this case ACCESS_COARSE_LOCATION
100         // is still grantable.
101         return group.foreground.isUserFixed && group.permissions[perm]?.isUserFixed == true
102     }
103 
supportsLocationAccuracynull104     private fun supportsLocationAccuracy(group: LightAppPermGroup): Boolean {
105         return KotlinUtils.isLocationAccuracyEnabled() &&
106             group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S
107     }
108 
isFineLocationHighlightednull109     private fun isFineLocationHighlighted(group: LightAppPermGroup): Boolean {
110         // Steps to decide location accuracy default state
111         // 1. If none of the FINE and COARSE isSelectedLocationAccuracy
112         //    flags is set, then use default precision from device config.
113         // 2. Otherwise set to whichever isSelectedLocationAccuracy is true.
114         val coarseLocationPerm = group.allPermissions[ACCESS_COARSE_LOCATION]
115         val fineLocationPerm = group.allPermissions[ACCESS_FINE_LOCATION]
116         return if (
117             coarseLocationPerm?.isSelectedLocationAccuracy == false &&
118                 fineLocationPerm?.isSelectedLocationAccuracy == false
119         ) {
120             // default location precision is true, indicates FINE
121             return true
122         } else {
123             fineLocationPerm?.isSelectedLocationAccuracy == true
124         }
125     }
126 }
127