blob: 80c3e5eaff151e32879254988934c100d04ef028 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.camera
import android.app.ActivityManager
import android.app.IActivityTaskManager
import android.content.ComponentName
import android.content.ContentResolver
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
class CameraGestureHelperTest : SysuiTestCase() {
@Mock
lateinit var centralSurfaces: CentralSurfaces
@Mock
lateinit var keyguardStateController: KeyguardStateController
@Mock
lateinit var packageManager: PackageManager
@Mock
lateinit var activityManager: ActivityManager
@Mock
lateinit var activityStarter: ActivityStarter
@Mock
lateinit var activityIntentHelper: ActivityIntentHelper
@Mock
lateinit var activityTaskManager: IActivityTaskManager
@Mock
lateinit var cameraIntents: CameraIntentsWrapper
@Mock
lateinit var contentResolver: ContentResolver
@Mock
lateinit var userTracker: UserTracker
private lateinit var underTest: CameraGestureHelper
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(cameraIntents.getSecureCameraIntent()).thenReturn(
Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
)
whenever(cameraIntents.getInsecureCameraIntent()).thenReturn(
Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
)
prepare()
underTest = CameraGestureHelper(
context = mock(),
centralSurfaces = centralSurfaces,
keyguardStateController = keyguardStateController,
packageManager = packageManager,
activityManager = activityManager,
activityStarter = activityStarter,
activityIntentHelper = activityIntentHelper,
activityTaskManager = activityTaskManager,
cameraIntents = cameraIntents,
contentResolver = contentResolver,
uiExecutor = MoreExecutors.directExecutor(),
userTracker = userTracker,
)
}
/**
* Prepares for tests by setting up the various mocks to emulate a specific device state.
*
* <p>Safe to call multiple times in a single test (for example, once in [setUp] and once in the
* actual test case).
*
* @param isCameraAllowedByAdmin Whether the device administrator allows use of the camera app
* @param installedCameraAppCount The number of installed camera apps on the device
* @param isUsingSecureScreenLockOption Whether the user-controlled setting for Screen Lock is
* set with a "secure" option that requires the user to provide some secret/credentials to be
* able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of
* non-secure options are "None" and "Swipe"
* @param isCameraActivityRunningOnTop Whether the camera activity is running at the top of the
* most recent/current task of activities
* @param isTaskListEmpty Whether there are no active activity tasks at all. Note that this is
* treated as `false` if [isCameraActivityRunningOnTop] is set to `true`
*/
private fun prepare(
isCameraAllowedByAdmin: Boolean = true,
installedCameraAppCount: Int = 1,
isUsingSecureScreenLockOption: Boolean = true,
isCameraActivityRunningOnTop: Boolean = false,
isTaskListEmpty: Boolean = false,
) {
whenever(centralSurfaces.isCameraAllowedByAdmin).thenReturn(isCameraAllowedByAdmin)
whenever(activityIntentHelper.wouldLaunchResolverActivity(any(), anyInt()))
.thenReturn(installedCameraAppCount > 1)
whenever(keyguardStateController.isMethodSecure).thenReturn(isUsingSecureScreenLockOption)
whenever(keyguardStateController.canDismissLockScreen())
.thenReturn(!isUsingSecureScreenLockOption)
if (installedCameraAppCount >= 1) {
val resolveInfo = ResolveInfo().apply {
this.activityInfo = ActivityInfo().apply {
packageName = CAMERA_APP_PACKAGE_NAME
}
}
whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
resolveInfo
)
} else {
whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
null
)
}
when {
isCameraActivityRunningOnTop -> {
val runningTaskInfo = ActivityManager.RunningTaskInfo().apply {
topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity")
}
whenever(activityManager.getRunningTasks(anyInt())).thenReturn(
listOf(
runningTaskInfo
)
)
}
isTaskListEmpty -> {
whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList())
}
else -> {
whenever(activityManager.getRunningTasks(anyInt())).thenReturn(listOf())
}
}
}
@Test
fun `canCameraGestureBeLaunched - status bar state is keyguard - returns true`() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
}
@Test
fun `canCameraGestureBeLaunched - state is shade-locked - returns true`() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
}
@Test
fun `canCameraGestureBeLaunched - state is keyguard - camera activity on top - returns true`() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
}
@Test
fun `canCameraGestureBeLaunched - state is shade-locked - camera activity on top - true`() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
}
@Test
fun `canCameraGestureBeLaunched - not allowed by admin - returns false`() {
prepare(isCameraAllowedByAdmin = false)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
}
@Test
fun `canCameraGestureBeLaunched - intent does not resolve to any app - returns false`() {
prepare(installedCameraAppCount = 0)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
}
@Test
fun `canCameraGestureBeLaunched - state is shade - no running tasks - returns true`() {
prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
}
@Test
fun `canCameraGestureBeLaunched - state is shade - camera activity on top - returns false`() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse()
}
@Test
fun `canCameraGestureBeLaunched - state is shade - camera activity not on top - true`() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
}
@Test
fun `launchCamera - only one camera app installed - using secure screen lock option`() {
val source = 1337
underTest.launchCamera(source)
assertActivityStarting(isSecure = true, source = source)
}
@Test
fun `launchCamera - only one camera app installed - using non-secure screen lock option`() {
prepare(isUsingSecureScreenLockOption = false)
val source = 1337
underTest.launchCamera(source)
assertActivityStarting(isSecure = false, source = source)
}
@Test
fun `launchCamera - multiple camera apps installed - using secure screen lock option`() {
prepare(installedCameraAppCount = 2)
val source = 1337
underTest.launchCamera(source)
assertActivityStarting(
isSecure = true,
source = source,
moreThanOneCameraAppInstalled = true
)
}
@Test
fun `launchCamera - multiple camera apps installed - using non-secure screen lock option`() {
prepare(
isUsingSecureScreenLockOption = false,
installedCameraAppCount = 2,
)
val source = 1337
underTest.launchCamera(source)
assertActivityStarting(
isSecure = false,
moreThanOneCameraAppInstalled = true,
source = source
)
}
private fun assertActivityStarting(
isSecure: Boolean,
source: Int,
moreThanOneCameraAppInstalled: Boolean = false,
) {
val intentCaptor = KotlinArgumentCaptor(Intent::class.java)
if (isSecure && !moreThanOneCameraAppInstalled) {
verify(activityTaskManager).startActivityAsUser(
any(),
any(),
any(),
intentCaptor.capture(),
any(),
any(),
any(),
anyInt(),
anyInt(),
any(),
any(),
anyInt()
)
} else {
verify(activityStarter).startActivity(intentCaptor.capture(), eq(false))
}
val intent = intentCaptor.value
assertThat(CameraIntents.isSecureCameraIntent(intent)).isEqualTo(isSecure)
assertThat(intent.getIntExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, -1))
.isEqualTo(source)
}
companion object {
private const val CAMERA_APP_PACKAGE_NAME = "cameraAppPackageName"
}
}