1 /*
2  * Copyright (C) 2019 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 android.permissionpolicy.cts
18 
19 import android.Manifest.permission.ACCEPT_HANDOVER
20 import android.Manifest.permission.ACCESS_COARSE_LOCATION
21 import android.Manifest.permission.ACCESS_FINE_LOCATION
22 import android.Manifest.permission.ACTIVITY_RECOGNITION
23 import android.Manifest.permission.ADD_VOICEMAIL
24 import android.Manifest.permission.ANSWER_PHONE_CALLS
25 import android.Manifest.permission.BLUETOOTH_ADVERTISE
26 import android.Manifest.permission.BLUETOOTH_CONNECT
27 import android.Manifest.permission.BLUETOOTH_SCAN
28 import android.Manifest.permission.BODY_SENSORS
29 import android.Manifest.permission.CALL_PHONE
30 import android.Manifest.permission.CAMERA
31 import android.Manifest.permission.GET_ACCOUNTS
32 import android.Manifest.permission.NEARBY_WIFI_DEVICES
33 import android.Manifest.permission.PACKAGE_USAGE_STATS
34 import android.Manifest.permission.POST_NOTIFICATIONS
35 import android.Manifest.permission.PROCESS_OUTGOING_CALLS
36 import android.Manifest.permission.RANGING
37 import android.Manifest.permission.READ_CALENDAR
38 import android.Manifest.permission.READ_CALL_LOG
39 import android.Manifest.permission.READ_CELL_BROADCASTS
40 import android.Manifest.permission.READ_CONTACTS
41 import android.Manifest.permission.READ_EXTERNAL_STORAGE
42 import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
43 import android.Manifest.permission.READ_PHONE_NUMBERS
44 import android.Manifest.permission.READ_PHONE_STATE
45 import android.Manifest.permission.READ_SMS
46 import android.Manifest.permission.RECEIVE_MMS
47 import android.Manifest.permission.RECEIVE_SMS
48 import android.Manifest.permission.RECEIVE_WAP_PUSH
49 import android.Manifest.permission.RECORD_AUDIO
50 import android.Manifest.permission.SEND_SMS
51 import android.Manifest.permission.USE_SIP
52 import android.Manifest.permission.UWB_RANGING
53 import android.Manifest.permission.WRITE_CALENDAR
54 import android.Manifest.permission.WRITE_CALL_LOG
55 import android.Manifest.permission.WRITE_CONTACTS
56 import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
57 import android.Manifest.permission_group.UNDEFINED
58 import android.app.AppOpsManager.permissionToOp
59 import android.content.pm.PackageManager.GET_PERMISSIONS
60 import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS
61 import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP
62 import android.health.connect.HealthPermissions
63 import android.os.Build
64 import android.permission.flags.Flags
65 import android.permission.PermissionManager
66 import androidx.test.platform.app.InstrumentationRegistry
67 import androidx.test.runner.AndroidJUnit4
68 import com.google.common.truth.Truth.assertThat
69 import com.google.common.truth.Truth.assertWithMessage
70 import org.junit.Test
71 import org.junit.runner.RunWith
72 
73 @RunWith(AndroidJUnit4::class)
74 class RuntimePermissionProperties {
75     private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
76     private val pm = context.packageManager
77 
78     private val platformPkg = pm.getPackageInfo("android", GET_PERMISSIONS)
79     private val platformRuntimePerms =
<lambda>null80         platformPkg.permissions!!.filter { it.protection == PROTECTION_DANGEROUS }
<lambda>null81     private val platformBgPermNames = platformRuntimePerms.mapNotNull { it.backgroundPermission }
82 
83     @Test
allRuntimeForegroundPermissionNeedAnAppOpnull84     fun allRuntimeForegroundPermissionNeedAnAppOp() {
85         val platformFgPerms = platformRuntimePerms.filter { !platformBgPermNames.contains(it.name) }
86 
87         for (perm in platformFgPerms) {
88             assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNotNull()
89         }
90     }
91 
92     @Test
groupOfRuntimePermissionsShouldBeUnknownnull93     fun groupOfRuntimePermissionsShouldBeUnknown() {
94         for (perm in platformRuntimePerms) {
95             assertWithMessage("Group of ${perm.name}").that(perm.group).isEqualTo(UNDEFINED)
96         }
97     }
98 
99     @Test
allAppOpPermissionNeedAnAppOpnull100     fun allAppOpPermissionNeedAnAppOp() {
101         val platformAppOpPerms =
102             platformPkg.permissions!!
103                 .filter { (it.protectionFlags and PROTECTION_FLAG_APPOP) != 0 }
104                 .filter {
105                     // Grandfather incomplete definition of PACKAGE_USAGE_STATS
106                     it.name != PACKAGE_USAGE_STATS
107                 }
108 
109         for (perm in platformAppOpPerms) {
110             assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNotNull()
111         }
112     }
113 
114     /** The permission of a background permission is the one of its foreground permission */
115     @Test
allRuntimeBackgroundPermissionCantHaveAnAppOpnull116     fun allRuntimeBackgroundPermissionCantHaveAnAppOp() {
117         val platformBgPerms = platformRuntimePerms.filter { platformBgPermNames.contains(it.name) }
118 
119         for (perm in platformBgPerms) {
120             assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNull()
121         }
122     }
123 
124     /** Commonly a new runtime permission is created by splitting an old one into twice */
125     @Test
runtimePermissionsShouldHaveBeenSplitFromPreviousPermissionnull126     fun runtimePermissionsShouldHaveBeenSplitFromPreviousPermission() {
127         // Runtime permissions in Android P
128         val expectedPerms =
129             mutableSetOf(
130                 READ_CONTACTS,
131                 WRITE_CONTACTS,
132                 GET_ACCOUNTS,
133                 READ_CALENDAR,
134                 WRITE_CALENDAR,
135                 SEND_SMS,
136                 RECEIVE_SMS,
137                 READ_SMS,
138                 RECEIVE_MMS,
139                 RECEIVE_WAP_PUSH,
140                 READ_CELL_BROADCASTS,
141                 READ_EXTERNAL_STORAGE,
142                 WRITE_EXTERNAL_STORAGE,
143                 ACCESS_FINE_LOCATION,
144                 ACCESS_COARSE_LOCATION,
145                 READ_CALL_LOG,
146                 WRITE_CALL_LOG,
147                 PROCESS_OUTGOING_CALLS,
148                 READ_PHONE_STATE,
149                 READ_PHONE_NUMBERS,
150                 CALL_PHONE,
151                 ADD_VOICEMAIL,
152                 USE_SIP,
153                 ANSWER_PHONE_CALLS,
154                 ACCEPT_HANDOVER,
155                 RECORD_AUDIO,
156                 CAMERA,
157                 BODY_SENSORS
158             )
159 
160         // Add permission split since P
161         for (sdkVersion in Build.VERSION_CODES.P + 1..Build.VERSION_CODES.CUR_DEVELOPMENT + 1) {
162             for (splitPerm in
163                 context.getSystemService(PermissionManager::class.java)!!.splitPermissions) {
164                 if (
165                     splitPerm.targetSdk == sdkVersion &&
166                         expectedPerms.contains(splitPerm.splitPermission)
167                 ) {
168                     expectedPerms.addAll(splitPerm.newPermissions)
169                 }
170             }
171         }
172 
173         // Add runtime permission added in Q which were _not_ split from a previously existing
174         // runtime permission
175         expectedPerms.add(ACTIVITY_RECOGNITION)
176 
177         // Add runtime permissions added in S which were _not_ split from a previously existing
178         // runtime permission
179         expectedPerms.add(BLUETOOTH_ADVERTISE)
180         expectedPerms.add(BLUETOOTH_CONNECT)
181         expectedPerms.add(BLUETOOTH_SCAN)
182         expectedPerms.add(UWB_RANGING)
183 
184         // Add runtime permissions added in T which were _not_ split from a previously existing
185         // runtime permission
186         expectedPerms.add(POST_NOTIFICATIONS)
187         expectedPerms.add(NEARBY_WIFI_DEVICES)
188 
189         // Add runtime permissions added in U which were _not_ split from a previously existing
190         // runtime permission
191         expectedPerms.add(READ_MEDIA_VISUAL_USER_SELECTED)
192 
193         // Add runtime permissions added in B which were _not_ split from a previously existing
194         // runtime permission
195         if (Flags.rangingPermissionEnabled()) {
196             expectedPerms.add(RANGING)
197         }
198 
199         // Separately check health permissions.
200         if (Flags.replaceBodySensorPermissionEnabled()) {
201             assertThat(expectedPerms).contains(HealthPermissions.READ_HEART_RATE);
202             assertThat(expectedPerms).contains(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
203 
204             // Remove these from the expected list once we've confirmed their
205             // present. These are not permissions owned by "android" so won't be
206             // in the list of platform runtime permissions.
207             expectedPerms.remove(HealthPermissions.READ_HEART_RATE);
208             expectedPerms.remove(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
209         }
210 
211         assertThat(platformRuntimePerms.map { it.name }).containsExactlyElementsIn(expectedPerms)
212     }
213 }
214