1 /*
2  * Copyright (C) 2022 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.permissionui.ui.handheld
18 
19 import android.Manifest.permission.ACCESS_COARSE_LOCATION
20 import android.content.Intent
21 import android.content.pm.PackageManager
22 import android.permission.cts.PermissionUtils.grantPermission
23 import android.permission.cts.PermissionUtils.install
24 import android.permission.cts.PermissionUtils.revokePermission
25 import android.permission.cts.PermissionUtils.uninstallApp
26 import android.platform.test.annotations.RequiresFlagsEnabled
27 import android.platform.test.flag.junit.DeviceFlagsValueProvider
28 import android.util.Log
29 import androidx.test.ext.junit.runners.AndroidJUnit4
30 import androidx.test.uiautomator.By
31 import com.android.compatibility.common.util.SystemUtil.eventually
32 import com.android.compatibility.common.util.SystemUtil.getEventually
33 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
34 import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
35 import com.android.permission.flags.Flags
36 import com.android.permissioncontroller.permissionui.getUsageCountsFromUi
37 import com.android.permissioncontroller.permissionui.wakeUpScreen
38 import com.google.common.truth.Truth.assertThat
39 import org.junit.After
40 import org.junit.Assert.assertNull
41 import org.junit.Before
42 import org.junit.Rule
43 import org.junit.Test
44 import org.junit.runner.RunWith
45 
46 /** Simple tests for {@link ManageStandardPermissionsFragment} */
47 @RunWith(AndroidJUnit4::class)
48 class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
49 
50     @JvmField @Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
51 
52     @Before
setupnull53     fun setup() {
54         wakeUpScreen()
55 
56         runWithShellPermissionIdentity {
57             removePackageIfInstalled()
58             instrumentationContext.startActivity(
59                 Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
60                     addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
61                 }
62             )
63         }
64     }
65 
66     @After
tearDownnull67     fun tearDown() {
68         uninstallApp(LOCATION_USER_PKG)
69         uninstallApp(ADDITIONAL_DEFINER_PKG)
70         uninstallApp(ADDITIONAL_USER_PKG)
71         uiDevice.pressBack()
72     }
73 
74     /**
75      * The test packages are not expected to be installed already, remove them if they are already
76      * installed (i.e. leftover from another test) when a test starts.
77      */
removePackageIfInstallednull78     private fun removePackageIfInstalled() {
79         val packageNames = listOf(LOCATION_USER_PKG, ADDITIONAL_DEFINER_PKG, ADDITIONAL_USER_PKG)
80         for (packageName in packageNames) {
81             try {
82                 val packageInfo =
83                     instrumentationContext.packageManager.getPackageInfo(packageName, 0)
84                 if (packageInfo != null) {
85                     Log.w(LOG_TAG, "package $packageName not expected to be installed.")
86                     uninstallApp(packageName)
87                     Thread.sleep(1000)
88                 }
89             } catch (e: PackageManager.NameNotFoundException) {
90                 // ignore
91             }
92         }
93     }
94 
95     /**
96      * Read the number of additional permissions from the Ui.
97      *
98      * @return number of additional permissions
99      */
getAdditionalPermissionCountnull100     private fun getAdditionalPermissionCount(): Int {
101         waitFindObjectOrNull(By.textContains(ADDITIONAL_PERMISSIONS_LABEL)) ?: return 0
102         // Sometimes the entire preference disappears while it's searching for the app count
103         // (during uninstalling). Hence also return the count as 0 if count doesn't exist
104         val additionalPermissionsSummary =
105             waitFindObjectOrNull(By.textContains(ADDITIONAL_PERMISSIONS_SUMMARY)) ?: return 0
106         val additionalPermissionsSummaryText = additionalPermissionsSummary.getText()
107 
108         // Matches a single number out of the summary line, i.e. "...3..." -> "3"
109         return getEventually {
110             Regex("^[^\\d]*(\\d+)[^\\d]*\$")
111                 .find(additionalPermissionsSummaryText)!!
112                 .groupValues[1]
113                 .toInt()
114         }
115     }
116 
117     @Test
groupSummaryGetsUpdatedWhenAppGetsInstallednull118     fun groupSummaryGetsUpdatedWhenAppGetsInstalled() {
119         val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
120 
121         install(LOCATION_USER_APK)
122         eventually(
123             {
124                 val afterInstall = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
125                 assertThat(afterInstall.granted).isEqualTo(original.granted)
126                 assertThat(afterInstall.total).isEqualTo(original.total + 1)
127             },
128             TIMEOUT,
129         )
130     }
131 
132     @Test
groupSummaryGetsUpdatedWhenAppGetsUninstallednull133     fun groupSummaryGetsUpdatedWhenAppGetsUninstalled() {
134         val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
135         install(LOCATION_USER_APK)
136         eventually(
137             { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isNotEqualTo(original) },
138             TIMEOUT,
139         )
140 
141         uninstallApp(LOCATION_USER_PKG)
142         eventually(
143             { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isEqualTo(original) },
144             TIMEOUT,
145         )
146     }
147 
148     @Test
groupSummaryGetsUpdatedWhenPermissionGetsGrantednull149     fun groupSummaryGetsUpdatedWhenPermissionGetsGranted() {
150         val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
151 
152         install(LOCATION_USER_APK)
153         eventually(
154             {
155                 assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total)
156                     .isEqualTo(original.total + 1)
157             },
158             TIMEOUT,
159         )
160 
161         grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
162         eventually {
163             assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
164                 .isEqualTo(original.granted + 1)
165         }
166     }
167 
168     @Test
groupSummaryGetsUpdatedWhenPermissionGetsRevokednull169     fun groupSummaryGetsUpdatedWhenPermissionGetsRevoked() {
170         val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
171 
172         install(LOCATION_USER_APK)
173         grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
174         eventually(
175             {
176                 assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total)
177                     .isNotEqualTo(original.total)
178                 assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
179                     .isNotEqualTo(original.granted)
180             },
181             TIMEOUT,
182         )
183 
184         revokePermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
185         eventually {
186             assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
187                 .isEqualTo(original.granted)
188         }
189     }
190 
191     @Test
additionalPermissionSummaryGetUpdateWhenAppGetsInstallednull192     fun additionalPermissionSummaryGetUpdateWhenAppGetsInstalled() {
193         val additionalPermissionBefore = getAdditionalPermissionCount()
194 
195         install(ADDITIONAL_DEFINER_APK)
196         install(ADDITIONAL_USER_APK)
197         eventually(
198             {
199                 assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore + 1)
200             },
201             TIMEOUT,
202         )
203     }
204 
205     @Test
additionalPermissionSummaryGetUpdateWhenUserGetsUninstallednull206     fun additionalPermissionSummaryGetUpdateWhenUserGetsUninstalled() {
207         val additionalPermissionBefore = getAdditionalPermissionCount()
208 
209         install(ADDITIONAL_DEFINER_APK)
210         install(ADDITIONAL_USER_APK)
211         eventually(
212             { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
213             TIMEOUT,
214         )
215 
216         uninstallApp(ADDITIONAL_USER_PKG)
217         eventually(
218             { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
219             TIMEOUT,
220         )
221     }
222 
223     @Test
additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstallednull224     fun additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstalled() {
225         val additionalPermissionBefore = getAdditionalPermissionCount()
226 
227         install(ADDITIONAL_DEFINER_APK)
228         install(ADDITIONAL_USER_APK)
229         eventually(
230             { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
231             TIMEOUT,
232         )
233 
234         uninstallApp(ADDITIONAL_DEFINER_PKG)
235         eventually(
236             { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
237             TIMEOUT,
238         )
239     }
240 
241     @Test
242     @RequiresFlagsEnabled(Flags.FLAG_DECLUTTERED_PERMISSION_MANAGER_ENABLED)
noUnusedPermissionGroupDisplayedInTheMainPagenull243     fun noUnusedPermissionGroupDisplayedInTheMainPage() {
244         eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0 of 0")))) }
245         eventually { assertNull(waitFindObjectOrNull(By.hasChild(By.textStartsWith("0/0")))) }
246     }
247 
248     companion object {
249         private val LOG_TAG = ManageStandardPermissionsFragmentTest::class.java.simpleName
250 
251         private const val LOCATION_USER_APK =
252             "/data/local/tmp/pc-permissionui/AppThatRequestsLocation.apk"
253         private const val ADDITIONAL_DEFINER_APK =
254             "/data/local/tmp/pc-permissionui/" + "PermissionUiDefineAdditionalPermissionApp.apk"
255         private const val ADDITIONAL_USER_APK =
256             "/data/local/tmp/pc-permissionui/" + "PermissionUiUseAdditionalPermissionApp.apk"
257         private const val LOCATION_USER_PKG = "android.permission.cts.appthatrequestpermission"
258         private const val ADDITIONAL_DEFINER_PKG =
259             "com.android.permissioncontroller.tests.appthatdefinespermission"
260         private const val ADDITIONAL_USER_PKG =
261             "com.android.permissioncontroller.tests.appthatrequestpermission"
262         private const val ADDITIONAL_PERMISSIONS_LABEL = "Additional permissions"
263         private const val ADDITIONAL_PERMISSIONS_SUMMARY = "more"
264         private const val LOCATION_GROUP_LABEL = "Location"
265 
266         // Package Added/Removed broadcast are pretty slow on cf devices, we may want to increase
267         // this in future if the test still fails.
268         private const val TIMEOUT = 30000L
269     }
270 }
271