1 /* 2 * Copyright (C) 2021 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.systemui.camera 18 19 import android.app.ActivityManager 20 import android.app.IActivityTaskManager 21 import android.content.ComponentName 22 import android.content.ContentResolver 23 import android.content.Intent 24 import android.content.pm.ActivityInfo 25 import android.content.pm.PackageManager 26 import android.content.pm.ResolveInfo 27 import androidx.test.filters.SmallTest 28 import com.android.systemui.ActivityIntentHelper 29 import com.android.systemui.SysuiTestCase 30 import com.android.systemui.plugins.ActivityStarter 31 import com.android.systemui.settings.UserTracker 32 import com.android.systemui.statusbar.StatusBarState 33 import com.android.systemui.statusbar.phone.CentralSurfaces 34 import com.android.systemui.statusbar.policy.KeyguardStateController 35 import com.android.systemui.util.mockito.KotlinArgumentCaptor 36 import com.android.systemui.util.mockito.eq 37 import com.android.systemui.util.mockito.mock 38 import com.google.common.truth.Truth.assertThat 39 import com.google.common.util.concurrent.MoreExecutors 40 import org.junit.Before 41 import org.junit.Test 42 import org.junit.runner.RunWith 43 import org.junit.runners.JUnit4 44 import org.mockito.Mock 45 import org.mockito.Mockito.any 46 import org.mockito.Mockito.anyInt 47 import org.mockito.Mockito.verify 48 import org.mockito.Mockito.`when` as whenever 49 import org.mockito.MockitoAnnotations 50 51 @SmallTest 52 @RunWith(JUnit4::class) 53 class CameraGestureHelperTest : SysuiTestCase() { 54 55 @Mock 56 lateinit var centralSurfaces: CentralSurfaces 57 @Mock 58 lateinit var keyguardStateController: KeyguardStateController 59 @Mock 60 lateinit var packageManager: PackageManager 61 @Mock 62 lateinit var activityManager: ActivityManager 63 @Mock 64 lateinit var activityStarter: ActivityStarter 65 @Mock 66 lateinit var activityIntentHelper: ActivityIntentHelper 67 @Mock 68 lateinit var activityTaskManager: IActivityTaskManager 69 @Mock 70 lateinit var cameraIntents: CameraIntentsWrapper 71 @Mock 72 lateinit var contentResolver: ContentResolver 73 @Mock 74 lateinit var userTracker: UserTracker 75 76 private lateinit var underTest: CameraGestureHelper 77 78 @Before 79 fun setUp() { 80 MockitoAnnotations.initMocks(this) 81 whenever(cameraIntents.getSecureCameraIntent()).thenReturn( 82 Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION) 83 ) 84 whenever(cameraIntents.getInsecureCameraIntent()).thenReturn( 85 Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION) 86 ) 87 88 prepare() 89 90 underTest = CameraGestureHelper( 91 context = mock(), 92 centralSurfaces = centralSurfaces, 93 keyguardStateController = keyguardStateController, 94 packageManager = packageManager, 95 activityManager = activityManager, 96 activityStarter = activityStarter, 97 activityIntentHelper = activityIntentHelper, 98 activityTaskManager = activityTaskManager, 99 cameraIntents = cameraIntents, 100 contentResolver = contentResolver, 101 uiExecutor = MoreExecutors.directExecutor(), 102 userTracker = userTracker, 103 ) 104 } 105 106 /** 107 * Prepares for tests by setting up the various mocks to emulate a specific device state. 108 * 109 * <p>Safe to call multiple times in a single test (for example, once in [setUp] and once in the 110 * actual test case). 111 * 112 * @param isCameraAllowedByAdmin Whether the device administrator allows use of the camera app 113 * @param installedCameraAppCount The number of installed camera apps on the device 114 * @param isUsingSecureScreenLockOption Whether the user-controlled setting for Screen Lock is 115 * set with a "secure" option that requires the user to provide some secret/credentials to be 116 * able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of 117 * non-secure options are "None" and "Swipe" 118 * @param isCameraActivityRunningOnTop Whether the camera activity is running at the top of the 119 * most recent/current task of activities 120 * @param isTaskListEmpty Whether there are no active activity tasks at all. Note that this is 121 * treated as `false` if [isCameraActivityRunningOnTop] is set to `true` 122 */ 123 private fun prepare( 124 isCameraAllowedByAdmin: Boolean = true, 125 installedCameraAppCount: Int = 1, 126 isUsingSecureScreenLockOption: Boolean = true, 127 isCameraActivityRunningOnTop: Boolean = false, 128 isTaskListEmpty: Boolean = false, 129 ) { 130 whenever(centralSurfaces.isCameraAllowedByAdmin).thenReturn(isCameraAllowedByAdmin) 131 132 whenever(activityIntentHelper.wouldLaunchResolverActivity(any(), anyInt())) 133 .thenReturn(installedCameraAppCount > 1) 134 135 whenever(keyguardStateController.isMethodSecure).thenReturn(isUsingSecureScreenLockOption) 136 whenever(keyguardStateController.canDismissLockScreen()) 137 .thenReturn(!isUsingSecureScreenLockOption) 138 139 if (installedCameraAppCount >= 1) { 140 val resolveInfo = ResolveInfo().apply { 141 this.activityInfo = ActivityInfo().apply { 142 packageName = CAMERA_APP_PACKAGE_NAME 143 } 144 } 145 whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn( 146 resolveInfo 147 ) 148 } else { 149 whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn( 150 null 151 ) 152 } 153 154 when { 155 isCameraActivityRunningOnTop -> { 156 val runningTaskInfo = ActivityManager.RunningTaskInfo().apply { 157 topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity") 158 } 159 whenever(activityManager.getRunningTasks(anyInt())).thenReturn( 160 listOf( 161 runningTaskInfo 162 ) 163 ) 164 } 165 isTaskListEmpty -> { 166 whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList()) 167 } 168 else -> { 169 whenever(activityManager.getRunningTasks(anyInt())).thenReturn(listOf()) 170 } 171 } 172 } 173 174 @Test 175 fun canCameraGestureBeLaunched_statusBarStateIsKeyguard_returnsTrue() { 176 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue() 177 } 178 179 @Test 180 fun canCameraGestureBeLaunched_stateIsShadeLocked_returnsTrue() { 181 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue() 182 } 183 184 @Test 185 fun canCameraGestureBeLaunched_stateIsKeyguard_cameraActivityOnTop_returnsTrue() { 186 prepare(isCameraActivityRunningOnTop = true) 187 188 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue() 189 } 190 191 @Test 192 fun canCameraGestureBeLaunched_stateIsShadeLocked_cameraActivityOnTop_true() { 193 prepare(isCameraActivityRunningOnTop = true) 194 195 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue() 196 } 197 198 @Test 199 fun canCameraGestureBeLaunched_notAllowedByAdmin_returnsFalse() { 200 prepare(isCameraAllowedByAdmin = false) 201 202 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse() 203 } 204 205 @Test 206 fun canCameraGestureBeLaunched_intentDoesNotResolveToAnyApp_returnsFalse() { 207 prepare(installedCameraAppCount = 0) 208 209 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse() 210 } 211 212 @Test 213 fun canCameraGestureBeLaunched_stateIsShade_noRunningTasks_returnsTrue() { 214 prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true) 215 216 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue() 217 } 218 219 @Test 220 fun canCameraGestureBeLaunched_stateIsShade_cameraActivityOnTop_returnsFalse() { 221 prepare(isCameraActivityRunningOnTop = true) 222 223 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse() 224 } 225 226 @Test 227 fun canCameraGestureBeLaunched_stateIsShade_cameraActivityNotOnTop_true() { 228 assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue() 229 } 230 231 @Test 232 fun launchCamera_onlyOneCameraAppInstalled_usingSecureScreenLockOption() { 233 val source = 1337 234 235 underTest.launchCamera(source) 236 237 assertActivityStarting(isSecure = true, source = source) 238 } 239 240 @Test 241 fun launchCamera_onlyOneCameraAppInstalled_usingNonSecureScreenLockOption() { 242 prepare(isUsingSecureScreenLockOption = false) 243 val source = 1337 244 245 underTest.launchCamera(source) 246 247 assertActivityStarting(isSecure = false, source = source) 248 } 249 250 @Test 251 fun launchCamera_multipleCameraAppsInstalled_usingSecureScreenLockOption() { 252 prepare(installedCameraAppCount = 2) 253 val source = 1337 254 255 underTest.launchCamera(source) 256 257 assertActivityStarting( 258 isSecure = true, 259 source = source, 260 moreThanOneCameraAppInstalled = true 261 ) 262 } 263 264 @Test 265 fun launchCamera_multipleCameraAppsInstalled_usingNonSecureScreenLockOption() { 266 prepare( 267 isUsingSecureScreenLockOption = false, 268 installedCameraAppCount = 2, 269 ) 270 val source = 1337 271 272 underTest.launchCamera(source) 273 274 assertActivityStarting( 275 isSecure = false, 276 moreThanOneCameraAppInstalled = true, 277 source = source 278 ) 279 } 280 281 private fun assertActivityStarting( 282 isSecure: Boolean, 283 source: Int, 284 moreThanOneCameraAppInstalled: Boolean = false, 285 ) { 286 val intentCaptor = KotlinArgumentCaptor(Intent::class.java) 287 if (isSecure && !moreThanOneCameraAppInstalled) { 288 verify(activityTaskManager).startActivityAsUser( 289 any(), 290 any(), 291 any(), 292 intentCaptor.capture(), 293 any(), 294 any(), 295 any(), 296 anyInt(), 297 anyInt(), 298 any(), 299 any(), 300 anyInt() 301 ) 302 } else { 303 verify(activityStarter).startActivity(intentCaptor.capture(), eq(false)) 304 } 305 val intent = intentCaptor.value 306 307 assertThat(CameraIntents.isSecureCameraIntent(intent)).isEqualTo(isSecure) 308 assertThat(intent.getIntExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, -1)) 309 .isEqualTo(source) 310 } 311 312 companion object { 313 private const val CAMERA_APP_PACKAGE_NAME = "cameraAppPackageName" 314 } 315 } 316