1 /*
2  * Copyright (C) 2018 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.permission.cts;
18 
19 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
20 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
21 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
22 import static android.app.AppOpsManager.MODE_ALLOWED;
23 import static android.app.AppOpsManager.MODE_FOREGROUND;
24 import static android.app.AppOpsManager.MODE_IGNORED;
25 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
26 import static android.content.pm.PermissionInfo.PROTECTION_INTERNAL;
27 import static android.permission.cts.PermissionUtils.getAppOp;
28 import static android.permission.cts.PermissionUtils.grantPermission;
29 import static android.permission.cts.PermissionUtils.install;
30 import static android.permission.cts.PermissionUtils.uninstallApp;
31 
32 import static com.android.compatibility.common.util.SystemUtil.eventually;
33 
34 import static com.google.common.truth.Truth.assertWithMessage;
35 
36 import static org.junit.Assert.assertNotEquals;
37 import static org.junit.Assert.assertNotNull;
38 import static org.junit.Assert.assertTrue;
39 
40 import android.app.AppOpsManager;
41 import android.app.UiAutomation;
42 import android.content.Context;
43 import android.content.pm.PackageInfo;
44 import android.content.pm.PackageManager;
45 import android.content.pm.PermissionInfo;
46 import android.platform.test.annotations.AppModeFull;
47 import android.util.ArrayMap;
48 import android.util.Log;
49 
50 import androidx.test.platform.app.InstrumentationRegistry;
51 import androidx.test.runner.AndroidJUnit4;
52 
53 import org.junit.After;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 @RunWith(AndroidJUnit4.class)
58 public class BackgroundPermissionsTest {
59     private static final String LOG_TAG = BackgroundPermissionsTest.class.getSimpleName();
60 
61     /** The package name of all apps used in the test */
62     private static final String APP_PKG = "android.permission.cts.appthatrequestpermission";
63 
64     private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
65     private static final String APK_LOCATION_BACKGROUND_29 =
66             TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission29.apk";
67     private static final String APK_LOCATION_29v4 =
68             TMP_DIR + "CtsAppThatRequestsLocationPermission29v4.apk";
69 
70     private static final Context sContext =
71             InstrumentationRegistry.getInstrumentation().getTargetContext();
72     private static final UiAutomation sUiAutomation =
73             InstrumentationRegistry.getInstrumentation().getUiAutomation();
74 
75     @After
uninstallTestApp()76     public void uninstallTestApp() {
77         uninstallApp(APP_PKG);
78     }
79 
80     @Test
81     @AppModeFull(reason = "Instant apps cannot read properties of other packages")
verifybackgroundPermissionsProperties()82     public void verifybackgroundPermissionsProperties() throws Exception {
83         PackageInfo pkg = sContext.getPackageManager().getPackageInfo(
84                 "android", PackageManager.GET_PERMISSIONS);
85         ArrayMap<String, String> potentialBackgroundPermissionsToGroup = new ArrayMap<>();
86 
87         int numPermissions = pkg.permissions.length;
88         for (int i = 0; i < numPermissions; i++) {
89             PermissionInfo permission = pkg.permissions[i];
90 
91             // background permissions must be dangerous or ungrantable or role
92             if ((permission.getProtection() & PROTECTION_DANGEROUS) != 0
93                     || (permission.getProtection() == PROTECTION_INTERNAL
94                             && (permission.getProtectionFlags() == 0
95                     || permission.getProtectionFlags() == PermissionInfo.PROTECTION_FLAG_ROLE))) {
96                 potentialBackgroundPermissionsToGroup.put(permission.name, permission.group);
97             }
98         }
99 
100         for (int i = 0; i < numPermissions; i++) {
101             PermissionInfo permission = pkg.permissions[i];
102             String backgroundPermissionName = permission.backgroundPermission;
103 
104             if (backgroundPermissionName != null) {
105                 Log.i(LOG_TAG, permission.name + "->" + backgroundPermissionName);
106 
107                 // foreground permissions must be dangerous
108                 assertNotEquals(0, permission.getProtection() & PROTECTION_DANGEROUS);
109 
110                 // All foreground permissions need an app op
111                 assertNotNull(AppOpsManager.permissionToOp(permission.name));
112 
113                 // the background permission must exist
114                 assertTrue(potentialBackgroundPermissionsToGroup
115                         .containsKey(backgroundPermissionName));
116             }
117         }
118     }
119 
120     /**
121      * If a bg permission is lost during an upgrade, the app-op should downgrade to foreground
122      */
123     @Test
124     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
125             + "to grant permissions to them. Also instant apps are never updated, hence the test "
126             + "is useless.")
appOpGetsDowngradedWhenBgPermIsNotRequestedAnymore()127     public void appOpGetsDowngradedWhenBgPermIsNotRequestedAnymore() throws Exception {
128         install(APK_LOCATION_BACKGROUND_29);
129         grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
130 
131         install(APK_LOCATION_29v4);
132 
133         eventually(() -> assertWithMessage("foreground app-op").that(
134                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
135     }
136 
137     /**
138      * Make sure location switch-op is set if no location access is granted.
139      */
140     @Test
141     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
142             + "to grant permissions to them. Also instant apps are never updated, hence the test "
143             + "is useless.")
appOpIsSetIfNoLocPermIsGranted()144     public void appOpIsSetIfNoLocPermIsGranted() {
145         install(APK_LOCATION_BACKGROUND_29);
146 
147         // Wait until the system sets the app-op automatically
148         eventually(() -> assertWithMessage("loc app-op").that(
149                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_IGNORED));
150     }
151 
152     /**
153      * Make sure location switch-op is set if only coarse location is granted
154      */
155     @Test
156     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
157             + "to grant permissions to them. Also instant apps are never updated, hence the test "
158             + "is useless.")
appOpIsSetIfOnlyCoarseLocPermIsGranted()159     public void appOpIsSetIfOnlyCoarseLocPermIsGranted() {
160         install(APK_LOCATION_BACKGROUND_29);
161         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
162 
163         // Wait until the system sets the app-op automatically
164         eventually(() -> assertWithMessage("loc app-op").that(
165                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
166     }
167 
168     /**
169      * Make sure location switch-op is set if coarse location with background access is granted.
170      */
171     @Test
172     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
173             + "to grant permissions to them. Also instant apps are never updated, hence the test "
174             + "is useless.")
appOpIsSetIfCoarseAndBgLocPermIsGranted()175     public void appOpIsSetIfCoarseAndBgLocPermIsGranted() {
176         install(APK_LOCATION_BACKGROUND_29);
177         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
178         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
179 
180         // Wait until the system sets the app-op automatically
181         eventually(() -> assertWithMessage("loc app-op").that(
182                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
183     }
184 
185     /**
186      * Make sure location switch-op is set if only fine location is granted
187      */
188     @Test
189     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
190             + "to grant permissions to them. Also instant apps are never updated, hence the test "
191             + "is useless.")
appOpIsSetIfOnlyFineLocPermIsGranted()192     public void appOpIsSetIfOnlyFineLocPermIsGranted() {
193         install(APK_LOCATION_BACKGROUND_29);
194         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
195 
196         // Wait until the system sets the app-op automatically
197         // Fine location uses background location to limit access
198         eventually(() -> assertWithMessage("loc app-op").that(
199                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
200     }
201 
202     /**
203      * Make sure location switch-op is set if fine location with background access is granted.
204      */
205     @Test
206     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
207             + "to grant permissions to them. Also instant apps are never updated, hence the test "
208             + "is useless.")
appOpIsSetIfFineAndBgLocPermIsGranted()209     public void appOpIsSetIfFineAndBgLocPermIsGranted() {
210         install(APK_LOCATION_BACKGROUND_29);
211         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
212         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
213 
214         // Wait until the system sets the app-op automatically
215         eventually(() -> assertWithMessage("loc app-op").that(
216                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
217     }
218 
219     /**
220      * Make sure location switch-op is set if fine and coarse location access is granted.
221      */
222     @Test
223     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
224             + "to grant permissions to them. Also instant apps are never updated, hence the test "
225             + "is useless.")
appOpIsSetIfFineAndCoarseLocPermIsGranted()226     public void appOpIsSetIfFineAndCoarseLocPermIsGranted() {
227         install(APK_LOCATION_BACKGROUND_29);
228         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
229         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
230 
231         // Wait until the system sets the app-op automatically
232         eventually(() -> assertWithMessage("loc app-op").that(
233                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
234     }
235 
236     /**
237      * Make sure location switch-op is set if fine and coarse location with background access is
238      * granted.
239      */
240     @Test
241     @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
242             + "to grant permissions to them. Also instant apps are never updated, hence the test "
243             + "is useless.")
appOpIsSetIfFineCoarseAndBgLocPermIsGranted()244     public void appOpIsSetIfFineCoarseAndBgLocPermIsGranted() {
245         install(APK_LOCATION_BACKGROUND_29);
246         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
247         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
248         sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
249 
250         // Wait until the system sets the app-op automatically
251         eventually(() -> assertWithMessage("loc app-op").that(
252                 getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
253     }
254 }
255