Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 922c54c4 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] FRP support.

When FRP (factory reset protection) is active, System UI shouldn't be
visible, avoiding security holes and letting the setup wizard prompt the
new user of the device to enter the credentials of the previous user.

To achieve this in Flexiglass, we simply observe the
isFactoryResetProtectionActive state and make sure that, if it's true,
the visibility of the entire scene container is false.

Fix: 322197941
Test: manually made sure that SysUI is completely gone and cannot be
interacted with when I hard-coded
DeviceProvisionedController.isFrpActive to return true.
Test: I WASN'T ABLE TO actually trigger FRP to fully test the feature
despite multiple device factory resets
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT

Change-Id: I043507704ccb6ab9f4b828aef74a43c37c21bf53
parent b997300a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -76,6 +76,8 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobi
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -262,6 +264,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
                simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
                windowController = mock(),
                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
            )
        startable.start()

@@ -518,6 +521,17 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            assertCurrentScene(SceneKey.Lockscreen)
        }

    @Test
    fun factoryResetProtectionActive_isNotVisible() =
        testScope.runTest {
            val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
            assertThat(isVisible).isTrue()

            kosmos.fakeDeviceProvisioningRepository.setFactoryResetProtectionActive(isActive = true)

            assertThat(isVisible).isFalse()
        }

    /**
     * Asserts that the current scene in the view-model matches what's expected.
     *
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -112,6 +113,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
                simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                authenticationInteractor = dagger.Lazy { authenticationInteractor },
                windowController = windowController,
                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
            )
    }

+27 −17
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
import com.android.systemui.util.println
@@ -55,6 +56,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -81,6 +83,7 @@ constructor(
    private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
    private val authenticationInteractor: Lazy<AuthenticationInteractor>,
    private val windowController: NotificationShadeWindowController,
    private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
) : CoreStartable {

    override fun start() {
@@ -112,6 +115,11 @@ constructor(
    private fun hydrateVisibility() {
        applicationScope.launch {
            // TODO(b/296114544): Combine with some global hun state to make it visible!
            deviceProvisioningInteractor.isFactoryResetProtectionActive
                .flatMapLatest { isFrpActive ->
                    if (isFrpActive) {
                        flowOf(false to "Factory Reset Protection is active")
                    } else {
                        sceneInteractor.transitionState
                            .mapNotNull { state ->
                                when (state) {
@@ -132,6 +140,8 @@ constructor(
                                }
                            }
                            .distinctUntilChanged()
                    }
                }
                .collect { (isVisible, loggingReason) ->
                    sceneInteractor.setVisible(isVisible, loggingReason)
                }
+6 −5
Original line number Diff line number Diff line
@@ -25,9 +25,10 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.kotlin.combine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -44,7 +45,7 @@ class ShadeInteractorImpl
@Inject
constructor(
    @Application val scope: CoroutineScope,
    deviceProvisioningRepository: DeviceProvisioningRepository,
    deviceProvisioningInteractor: DeviceProvisioningInteractor,
    disableFlagsRepository: DisableFlagsRepository,
    dozeParams: DozeParameters,
    keyguardRepository: KeyguardRepository,
@@ -56,7 +57,7 @@ constructor(
) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
    override val isShadeEnabled: StateFlow<Boolean> =
        combine(
                deviceProvisioningRepository.isFactoryResetProtectionActive,
                deviceProvisioningInteractor.isFactoryResetProtectionActive,
                disableFlagsRepository.disableFlags,
            ) { isFrpActive, isDisabledByFlags ->
                isDisabledByFlags.isShadeEnabled() && !isFrpActive
@@ -83,7 +84,7 @@ constructor(
            powerInteractor.isAsleep,
            keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
            keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
            deviceProvisioningRepository.isFactoryResetProtectionActive,
            deviceProvisioningInteractor.isFactoryResetProtectionActive,
        ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
            when {
                // Touches are disabled when Factory Reset Protection is active
@@ -103,7 +104,7 @@ constructor(
            isShadeEnabled,
            keyguardRepository.isDozing,
            userSetupRepository.isUserSetUp,
            deviceProvisioningRepository.isDeviceProvisioned,
            deviceProvisioningInteractor.isDeviceProvisioned,
        ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
            isDeviceProvisioned &&
                // Disallow QS during setup if it's a simple user switcher. (The user intends to
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.statusbar.policy.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow

/** Encapsulates device-provisioning related business logic. */
@SysUISingleton
class DeviceProvisioningInteractor
@Inject
constructor(
    repository: DeviceProvisioningRepository,
) {
    /**
     * Whether this device has been provisioned.
     *
     * @see android.provider.Settings.Global.DEVICE_PROVISIONED
     */
    val isDeviceProvisioned: Flow<Boolean> = repository.isDeviceProvisioned

    /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
    val isFactoryResetProtectionActive: Flow<Boolean> = repository.isFactoryResetProtectionActive
}
Loading