Camera quick affordance respects device policy.
If the device admin policy disables the camera, neither the stills nor the video camera quick affordances are available. If stills one also respects the "secure camera" disablement flag, which the the video one does not need to, because it always requires an unlock to access the video camera (due to a missing camera API). Bug: 268218507 Test: unit tests added Test: temporarily faked DevicePolicyManager API and manually verified that the camera and video camera options do not show up in wallpaper picker. Change-Id: Ia5b68283a25bc686ab11258a4b55b001fabcf562
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import com.android.systemui.R
|
||||
@@ -27,10 +28,14 @@ import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import dagger.Lazy
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@SysUISingleton
|
||||
class CameraQuickAffordanceConfig
|
||||
@@ -39,6 +44,9 @@ constructor(
|
||||
@Application private val context: Context,
|
||||
private val packageManager: PackageManager,
|
||||
private val cameraGestureHelper: Lazy<CameraGestureHelper>,
|
||||
private val userTracker: UserTracker,
|
||||
private val devicePolicyManager: DevicePolicyManager,
|
||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||
) : KeyguardQuickAffordanceConfig {
|
||||
|
||||
override val key: String
|
||||
@@ -79,7 +87,12 @@ constructor(
|
||||
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
|
||||
}
|
||||
|
||||
private fun isLaunchable(): Boolean {
|
||||
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
|
||||
private suspend fun isLaunchable(): Boolean {
|
||||
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) &&
|
||||
withContext(backgroundDispatcher) {
|
||||
!devicePolicyManager.getCameraDisabled(null, userTracker.userId) &&
|
||||
devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) and
|
||||
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.android.systemui.ActivityIntentHelper
|
||||
@@ -29,10 +30,13 @@ import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@SysUISingleton
|
||||
class VideoCameraQuickAffordanceConfig
|
||||
@@ -42,6 +46,8 @@ constructor(
|
||||
private val cameraIntents: CameraIntentsWrapper,
|
||||
private val activityIntentHelper: ActivityIntentHelper,
|
||||
private val userTracker: UserTracker,
|
||||
private val devicePolicyManager: DevicePolicyManager,
|
||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||
) : KeyguardQuickAffordanceConfig {
|
||||
|
||||
private val intent: Intent by lazy {
|
||||
@@ -63,8 +69,8 @@ constructor(
|
||||
get() = R.drawable.ic_videocam
|
||||
|
||||
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
|
||||
get() =
|
||||
flowOf(
|
||||
get() = flow {
|
||||
emit(
|
||||
if (isLaunchable()) {
|
||||
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
|
||||
icon =
|
||||
@@ -77,6 +83,7 @@ constructor(
|
||||
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
|
||||
return if (isLaunchable()) {
|
||||
@@ -95,11 +102,14 @@ constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun isLaunchable(): Boolean {
|
||||
private suspend fun isLaunchable(): Boolean {
|
||||
return activityIntentHelper.getTargetActivityInfo(
|
||||
intent,
|
||||
userTracker.userId,
|
||||
true,
|
||||
) != null
|
||||
) != null &&
|
||||
withContext(backgroundDispatcher) {
|
||||
!devicePolicyManager.getCameraDisabled(null, userTracker.userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,19 @@
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.camera.CameraGestureHelper
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
@@ -44,21 +48,28 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() {
|
||||
@Mock private lateinit var cameraGestureHelper: CameraGestureHelper
|
||||
@Mock private lateinit var context: Context
|
||||
@Mock private lateinit var packageManager: PackageManager
|
||||
@Mock private lateinit var userTracker: UserTracker
|
||||
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
|
||||
|
||||
private lateinit var underTest: CameraQuickAffordanceConfig
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
setLaunchable(true)
|
||||
setLaunchable()
|
||||
|
||||
val testDispatcher = StandardTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
underTest =
|
||||
CameraQuickAffordanceConfig(
|
||||
context,
|
||||
packageManager,
|
||||
) {
|
||||
cameraGestureHelper
|
||||
}
|
||||
{ cameraGestureHelper },
|
||||
userTracker,
|
||||
devicePolicyManager,
|
||||
testDispatcher,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,23 +84,57 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - default when launchable`() = runTest {
|
||||
setLaunchable(true)
|
||||
fun `getPickerScreenState - default when launchable`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(true)
|
||||
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
|
||||
}
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when not launchable`() = runTest {
|
||||
setLaunchable(false)
|
||||
fun `getPickerScreenState - unavailable when camera app not installed`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isCameraAppInstalled = false)
|
||||
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
|
||||
private fun setLaunchable(isLaunchable: Boolean) {
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when camera disabled by admin`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isCameraDisabledByDeviceAdmin = true)
|
||||
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
|
||||
|
||||
Truth.assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
|
||||
private fun setLaunchable(
|
||||
isCameraAppInstalled: Boolean = true,
|
||||
isCameraDisabledByDeviceAdmin: Boolean = false,
|
||||
isSecureCameraDisabledByDeviceAdmin: Boolean = false,
|
||||
) {
|
||||
whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
|
||||
.thenReturn(isLaunchable)
|
||||
.thenReturn(isCameraAppInstalled)
|
||||
whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
|
||||
.thenReturn(isCameraDisabledByDeviceAdmin)
|
||||
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
|
||||
.thenReturn(
|
||||
if (isSecureCameraDisabledByDeviceAdmin) {
|
||||
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
|
||||
} else {
|
||||
0
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.ActivityIntentHelper
|
||||
@@ -24,11 +25,14 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.camera.CameraIntentsWrapper
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.settings.FakeUserTracker
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -44,59 +48,94 @@ import org.mockito.MockitoAnnotations
|
||||
class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var activityIntentHelper: ActivityIntentHelper
|
||||
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
|
||||
|
||||
private lateinit var underTest: VideoCameraQuickAffordanceConfig
|
||||
private lateinit var userTracker: UserTracker
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
val testDispatcher = StandardTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
userTracker = FakeUserTracker()
|
||||
underTest =
|
||||
VideoCameraQuickAffordanceConfig(
|
||||
context = context,
|
||||
cameraIntents = CameraIntentsWrapper(context),
|
||||
activityIntentHelper = activityIntentHelper,
|
||||
userTracker = FakeUserTracker(),
|
||||
userTracker = userTracker,
|
||||
devicePolicyManager = devicePolicyManager,
|
||||
backgroundDispatcher = testDispatcher,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lockScreenState - visible when launchable`() = runTest {
|
||||
setLaunchable(true)
|
||||
fun `lockScreenState - visible when launchable`() =
|
||||
testScope.runTest {
|
||||
setLaunchable()
|
||||
|
||||
val lockScreenState = collectLastValue(underTest.lockScreenState)
|
||||
val lockScreenState = collectLastValue(underTest.lockScreenState)
|
||||
|
||||
assertThat(lockScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
|
||||
}
|
||||
assertThat(lockScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lockScreenState - hidden when not launchable`() = runTest {
|
||||
setLaunchable(false)
|
||||
fun `lockScreenState - hidden when app not installed on device`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isVideoCameraAppInstalled = false)
|
||||
|
||||
val lockScreenState = collectLastValue(underTest.lockScreenState)
|
||||
val lockScreenState = collectLastValue(underTest.lockScreenState)
|
||||
|
||||
assertThat(lockScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
||||
}
|
||||
assertThat(lockScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - default when launchable`() = runTest {
|
||||
setLaunchable(true)
|
||||
fun `lockScreenState - hidden when camera disabled by admin`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isCameraDisabledByAdmin = true)
|
||||
|
||||
assertThat(underTest.getPickerScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
|
||||
}
|
||||
val lockScreenState = collectLastValue(underTest.lockScreenState)
|
||||
|
||||
assertThat(lockScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when not launchable`() = runTest {
|
||||
setLaunchable(false)
|
||||
fun `getPickerScreenState - default when launchable`() =
|
||||
testScope.runTest {
|
||||
setLaunchable()
|
||||
|
||||
assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
assertThat(underTest.getPickerScreenState())
|
||||
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
|
||||
}
|
||||
|
||||
private fun setLaunchable(isLaunchable: Boolean) {
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when app not installed on device`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isVideoCameraAppInstalled = false)
|
||||
|
||||
assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPickerScreenState - unavailable when camera disabled by admin`() =
|
||||
testScope.runTest {
|
||||
setLaunchable(isCameraDisabledByAdmin = true)
|
||||
|
||||
assertThat(underTest.getPickerScreenState())
|
||||
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
|
||||
private fun setLaunchable(
|
||||
isVideoCameraAppInstalled: Boolean = true,
|
||||
isCameraDisabledByAdmin: Boolean = false,
|
||||
) {
|
||||
whenever(
|
||||
activityIntentHelper.getTargetActivityInfo(
|
||||
any(),
|
||||
@@ -105,11 +144,13 @@ class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
|
||||
)
|
||||
)
|
||||
.thenReturn(
|
||||
if (isLaunchable) {
|
||||
if (isVideoCameraAppInstalled) {
|
||||
mock()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
|
||||
.thenReturn(isCameraDisabledByAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user