1 /*
2  * 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 android.permissionui.cts
18 
19 import android.Manifest.permission.ACCESS_FINE_LOCATION
20 import android.content.Intent
21 import android.content.pm.PackageManager
22 import android.graphics.Point
23 import android.os.Build
24 import androidx.test.filters.FlakyTest
25 import androidx.test.filters.SdkSuppress
26 import androidx.test.uiautomator.By
27 import com.android.compatibility.common.util.SystemUtil
28 import org.junit.Assume.assumeFalse
29 import org.junit.Before
30 import org.junit.Test
31 
32 /** Tests permissions can't be tapjacked */
33 @FlakyTest
34 class PermissionTapjackingTest : BaseUsePermissionTest() {
35 
36     @Before
installAppLatestnull37     fun installAppLatest() {
38         installPackage(APP_APK_PATH_WITH_OVERLAY)
39     }
40 
41     @Test
testTapjackGrantDialog_fullOverlaynull42     fun testTapjackGrantDialog_fullOverlay() {
43         // PermissionController for television uses a floating window.
44         assumeFalse(isTv)
45 
46         // Automotive split-screen multitasking uses multi-window mode
47         assumeFalse(isAutomotiveSplitscreen)
48 
49         assertAppHasPermission(ACCESS_FINE_LOCATION, false)
50         requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
51 
52         val buttonCenter =
53             waitFindObject(By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)))
54                 .visibleCenter
55 
56         // Wait for overlay to hide the dialog
57         context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY).putExtra(EXTRA_FULL_OVERLAY, true))
58         waitFindObject(By.res("android.permissionui.cts.usepermission:id/overlay"))
59 
60         tryClicking(buttonCenter)
61     }
62 
63     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
64     @Test
testTapjackGrantDialog_partialOverlaynull65     fun testTapjackGrantDialog_partialOverlay() {
66         // PermissionController for television uses a floating window.
67         assumeFalse(isTv)
68 
69         // Automotive split-screen multitasking uses multi-window mode
70         assumeFalse(isAutomotiveSplitscreen)
71 
72         assertAppHasPermission(ACCESS_FINE_LOCATION, false)
73         requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
74 
75         val foregroundButtonCenter =
76             waitFindObject(By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)))
77                 .visibleCenter
78         val oneTimeButton =
79             waitFindObjectOrNull(By.text(getPermissionControllerString(ALLOW_ONE_TIME_BUTTON_TEXT)))
80         // If one-time button is not available, fallback to deny button
81         val overlayButtonBounds =
82             oneTimeButton?.visibleBounds
83                 ?: waitFindObject(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
84                     .visibleBounds
85 
86         // Wait for overlay to hide the dialog
87         context.sendBroadcast(
88             Intent(ACTION_SHOW_OVERLAY)
89                 .putExtra(EXTRA_FULL_OVERLAY, false)
90                 .putExtra(OVERLAY_LEFT, overlayButtonBounds.left)
91                 .putExtra(OVERLAY_TOP, overlayButtonBounds.top)
92                 .putExtra(OVERLAY_RIGHT, overlayButtonBounds.right)
93                 .putExtra(OVERLAY_BOTTOM, overlayButtonBounds.bottom)
94         )
95         waitFindObject(By.res("android.permissionui.cts.usepermission:id/overlay"))
96 
97         tryClicking(foregroundButtonCenter)
98     }
99 
tryClickingnull100     private fun tryClicking(buttonCenter: Point) {
101         try {
102             // Try to grant the permission, this should fail
103             SystemUtil.eventually(
104                 {
105                     if (
106                         packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
107                             PackageManager.PERMISSION_DENIED
108                     ) {
109                         uiDevice.click(buttonCenter.x, buttonCenter.y)
110                         Thread.sleep(100)
111                     }
112                     assertAppHasPermission(ACCESS_FINE_LOCATION, true)
113                 },
114                 10000
115             )
116         } catch (e: RuntimeException) {
117             // expected
118         }
119         // Permission should not be granted
120         assertAppHasPermission(ACCESS_FINE_LOCATION, false)
121 
122         // Verify that clicking the dialog without the overlay still works
123         context.sendBroadcast(Intent(ACTION_HIDE_OVERLAY))
124         SystemUtil.eventually(
125             {
126                 if (
127                     packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
128                         PackageManager.PERMISSION_DENIED
129                 ) {
130                     uiDevice.click(buttonCenter.x, buttonCenter.y)
131                     Thread.sleep(100)
132                 }
133                 assertAppHasPermission(ACCESS_FINE_LOCATION, true)
134             },
135             10000
136         )
137     }
138 
139     companion object {
140         const val ACTION_SHOW_OVERLAY = "android.permissionui.cts.usepermission.ACTION_SHOW_OVERLAY"
141         const val ACTION_HIDE_OVERLAY = "android.permissionui.cts.usepermission.ACTION_HIDE_OVERLAY"
142 
143         const val EXTRA_FULL_OVERLAY = "android.permissionui.cts.usepermission.extra.FULL_OVERLAY"
144 
145         const val OVERLAY_LEFT = "android.permissionui.cts.usepermission.extra.OVERLAY_LEFT"
146         const val OVERLAY_TOP = "android.permissionui.cts.usepermission.extra.OVERLAY_TOP"
147         const val OVERLAY_RIGHT = "android.permissionui.cts.usepermission.extra.OVERLAY_RIGHT"
148         const val OVERLAY_BOTTOM = "android.permissionui.cts.usepermission.extra.OVERLAY_BOTTOM"
149     }
150 }
151