blob: ca94ea8267827d91b8fb2551a1b4d9429fe37c8f [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.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
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(),
)
}
/**
* 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(CameraGestureHelper.EXTRA_CAMERA_LAUNCH_SOURCE, -1))
.isEqualTo(source)
}
companion object {
private const val CAMERA_APP_PACKAGE_NAME = "cameraAppPackageName"
}
}