diff options
56 files changed, 1173 insertions, 685 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java index 92084e403846..67d46f4c6d73 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java @@ -16,21 +16,19 @@ package com.android.wm.shell.shared; -import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES; - import android.annotation.NonNull; import android.content.Context; import android.os.SystemProperties; -import android.provider.Settings; -import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; /** * Constants for desktop mode feature */ +// TODO(b/237575897): Move this file to the `com.android.wm.shell.shared.desktopmode` package public class DesktopModeStatus { private static final String TAG = "DesktopModeStatus"; @@ -178,19 +176,7 @@ public class DesktopModeStatus { public static boolean canEnterDesktopMode(@NonNull Context context) { if (!isDeviceEligibleForDesktopMode(context)) return false; - // If dev option has ever been manually toggled by the user, return its value - // TODO(b/348193756) : Move the logic for DW override based on toggle overides to a common - // infrastructure and add caching for the computation - int defaultOverrideState = -1; - int toggleState = Settings.Global.getInt(context.getContentResolver(), - DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, defaultOverrideState); - if (toggleState != defaultOverrideState) { - Log.d(TAG, "Using Desktop mode dev option overridden state"); - return toggleState != 0; - } - - // Return Desktop windowing flag value - return isDesktopModeFlagEnabled(); + return DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context); } /** diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt new file mode 100644 index 000000000000..dfc9c82d85ff --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -0,0 +1,110 @@ +/* + * 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.wm.shell.shared.desktopmode + +import android.content.Context +import android.provider.Settings +import android.util.Log +import com.android.window.flags.Flags +import com.android.wm.shell.shared.DesktopModeStatus + +/* + * A shared class to check desktop mode flags state. + * + * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag + * value and the developer option override state (if applicable). + **/ +enum class DesktopModeFlags( + // Function called to obtain aconfig flag value. + private val flagFunction: () -> Boolean, + // Whether the flag state should be affected by developer option. + private val shouldOverrideByDevOption: Boolean +) { + // All desktop mode related flags will be added here + DESKTOP_WINDOWING_MODE(DesktopModeStatus::isDesktopModeFlagEnabled, true); + + private val TAG = "DesktopModeFlags" + + // Cache for toggle override, which is initialized once on its first access. It needs to be refreshed + // only on reboots as overridden state takes effect on reboots. + private var cachedToggleOverride: ToggleOverride? = null + + /** + * Determines state of flag based on the actual flag and desktop mode developer option + * overrides. + * + * Note, this method makes sure that a constant developer toggle overrides is read until reboot. + */ + fun isEnabled(context: Context): Boolean = + if (!Flags.showDesktopWindowingDevOption() || + !shouldOverrideByDevOption || + context.contentResolver == null) { + flagFunction() + } else { + when (getToggleOverride(context)) { + ToggleOverride.OVERRIDE_UNSET -> flagFunction() + ToggleOverride.OVERRIDE_OFF -> false + ToggleOverride.OVERRIDE_ON -> true + } + } + + private fun getToggleOverride(context: Context): ToggleOverride { + val override = cachedToggleOverride ?: run { + // Cache toggle override the first time we encounter context. Override does not change + // with context, as context is just used to fetch a system property. + + // TODO(b/348193756): Cache a persistent value for Settings.Global until reboot. Current + // cache will change with process restart. + val toggleOverride = + Settings.Global.getInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, + ToggleOverride.OVERRIDE_UNSET.setting) + + val newOverride = + settingToToggleOverrideMap[toggleOverride] + ?: run { + Log.w(TAG, "Unknown toggleOverride $toggleOverride") + ToggleOverride.OVERRIDE_UNSET + } + cachedToggleOverride = newOverride + Log.d(TAG, "Toggle override initialized to: $newOverride") + newOverride + } + + return override + } + + // TODO(b/348193756): Share ToggleOverride enum with Settings + // 'DesktopModePreferenceController' + /** + * Override state of desktop mode developer option toggle. + * + * @property setting The integer value that is associated with the developer option toggle + * override + */ + enum class ToggleOverride(val setting: Int) { + /** No override is set. */ + OVERRIDE_UNSET(-1), + /** Override to off. */ + OVERRIDE_OFF(0), + /** Override to on. */ + OVERRIDE_ON(1) + } + + private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting } +} diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS new file mode 100644 index 000000000000..2fabd4a33586 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS @@ -0,0 +1 @@ +file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index a3b0dc5f5b44..1fa3f9582b40 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -264,7 +264,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun instantiate_flagOff_doNotAddInitCallback() { - whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false) + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) clearInvocations(shellInit) createController() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt new file mode 100644 index 000000000000..115b218f2e82 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt @@ -0,0 +1,225 @@ +/* + * 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.wm.shell.shared.desktopmode + +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.provider.Settings +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE +import com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.shared.DesktopModeStatus +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_ON +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +/** + * Test class for [DesktopModeFlags] + * + * Usage: atest WMShellUnitTests:DesktopModeFlagsTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopModeFlagsTest : ShellTestCase() { + + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Before + fun setUp() { + resetCache() + } + + // TODO(b/348193756): Add tests + // isEnabled_flagNotOverridable_overrideOff_featureFlagOn_returnsTrue and + // isEnabled_flagNotOverridable_overrideOn_featureFlagOff_returnsFalse after adding non + // overridable flags to DesktopModeFlags. + + @Test + @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() { + setOverride(OVERRIDE_OFF.setting) + + // In absence of dev options, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() { + setOverride(OVERRIDE_ON.setting) + + // In absence of dev options, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_unsetOverride_featureFlagOn_returnsTrue() { + setOverride(OVERRIDE_UNSET.setting) + + // For overridableFlag, for unset overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_unsetOverride_featureFlagOff_returnsFalse() { + setOverride(OVERRIDE_UNSET.setting) + + // For overridableFlag, for unset overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_noOverride_featureFlagOn_returnsTrue() { + setOverride(null) + + // For overridableFlag, in absence of overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_noOverride_featureFlagOff_returnsFalse() { + setOverride(null) + + // For overridableFlag, in absence of overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOn_returnsTrue() { + setOverride(-2) + + // For overridableFlag, for recognizable overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOff_returnsFalse() { + setOverride(-2) + + // For overridableFlag, for recognizable overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_overrideOff_featureFlagOn_returnsFalse() { + setOverride(OVERRIDE_OFF.setting) + + // For overridableFlag, follow override if they exist + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_overrideOn_featureFlagOff_returnsTrue() { + setOverride(OVERRIDE_ON.setting) + + // For overridableFlag, follow override if they exist + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() { + setOverride(OVERRIDE_OFF.setting) + + // For overridableFlag, follow override if they exist + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + + setOverride(OVERRIDE_ON.setting) + + // Keep overrides constant through the process + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() { + setOverride(OVERRIDE_ON.setting) + + // For overridableFlag, follow override if they exist + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + + setOverride(OVERRIDE_OFF.setting) + + // Keep overrides constant through the process + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_flagOverridable_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() { + setOverride(null) + // For overridableFlag, in absence of overrides, follow flag + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + + val mockitoSession: StaticMockitoSession = + ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + try { + // No caching of flags + whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false) + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + } finally { + mockitoSession.finishMocking() + } + } + + private fun setOverride(setting: Int?) { + val contentResolver = mContext.contentResolver + val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES + if (setting == null) { + Settings.Global.putString(contentResolver, key, null) + } else { + Settings.Global.putInt(contentResolver, key, setting) + } + } + + private fun resetCache() { + val cacheToggleOverride = + DESKTOP_WINDOWING_MODE::class.java.getDeclaredField("cachedToggleOverride") + cacheToggleOverride.isAccessible = true + cacheToggleOverride.set(DESKTOP_WINDOWING_MODE, null) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS new file mode 100644 index 000000000000..2fabd4a33586 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS @@ -0,0 +1 @@ +file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
\ No newline at end of file diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 40918a48f6c3..a8eccc5add1a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.DisableSceneContainer @@ -42,19 +41,15 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition -import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -67,36 +62,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val repository = kosmos.fakeKeyguardTransitionRepository val testScope = kosmos.testScope - private val sceneTransitions = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(Scenes.Lockscreen) - ) - - private val lsToGone = - ObservableTransitionState.Transition( - Scenes.Lockscreen, - Scenes.Gone, - flowOf(Scenes.Lockscreen), - flowOf(0f), - false, - flowOf(false) - ) - - private val goneToLs = - ObservableTransitionState.Transition( - Scenes.Gone, - Scenes.Lockscreen, - flowOf(Scenes.Lockscreen), - flowOf(0f), - false, - flowOf(false) - ) - - @Before - fun setUp() { - kosmos.sceneContainerRepository.setTransitionState(sceneTransitions) - } - @Test fun transitionCollectorsReceivesOnlyAppropriateEvents() = testScope.runTest { @@ -318,7 +283,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { @Test fun isInTransitionToAnyState() = testScope.runTest { - val inTransition by collectValues(underTest.isInTransitionToAnyState) + val inTransition by collectValues(underTest.isInTransition) assertEquals( listOf( @@ -374,9 +339,50 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test + @EnableSceneContainer + fun isInTransition_withScene() = + testScope.runTest { + val inTransition by collectValues(underTest.isInTransition) + + assertEquals( + listOf( + false, + true, // The repo is seeded with a transition from OFF to LOCKSCREEN. + false, + ), + inTransition + ) + + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer)) + + assertEquals( + listOf( + false, + true, + false, + true, + ), + inTransition + ) + + kosmos.setSceneTransition(Idle(Scenes.Bouncer)) + + assertEquals( + listOf( + false, + true, + false, + true, + false, + ), + inTransition + ) + } + + @Test fun isInTransitionToAnyState_finishedStateIsStartedStateAfterCancels() = testScope.runTest { - val inTransition by collectValues(underTest.isInTransitionToAnyState) + val inTransition by collectValues(underTest.isInTransition) assertEquals( listOf( @@ -731,92 +737,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test - fun isInTransitionFromStateWhere() = - testScope.runTest { - val results by collectValues(underTest.isInTransitionFromStateWhere { it == DOZING }) - - sendSteps( - TransitionStep(AOD, DOZING, 0f, STARTED), - TransitionStep(AOD, DOZING, 0.5f, RUNNING), - TransitionStep(AOD, DOZING, 1f, FINISHED), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - ) - ) - - sendSteps( - TransitionStep(DOZING, GONE, 0f, STARTED), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - true, - ) - ) - - sendSteps( - TransitionStep(DOZING, GONE, 0f, RUNNING), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - true, - ) - ) - - sendSteps( - TransitionStep(DOZING, GONE, 0f, FINISHED), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - true, - false, - ) - ) - - sendSteps( - TransitionStep(GONE, DOZING, 0f, STARTED), - TransitionStep(GONE, DOZING, 0f, RUNNING), - TransitionStep(GONE, DOZING, 1f, FINISHED), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - true, - false, - ) - ) - - sendSteps( - TransitionStep(DOZING, GONE, 0f, STARTED), - TransitionStep(DOZING, GONE, 0f, RUNNING), - ) - - assertThat(results) - .isEqualTo( - listOf( - false, - true, - false, - true, - ) - ) - } - - @Test fun isInTransitionWhere() = testScope.runTest { val results by @@ -1599,7 +1519,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val currentStatesConverted by collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED))) - sceneTransitions.value = lsToGone + kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) @@ -1615,7 +1535,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { testScope.runTest { val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE))) - sceneTransitions.value = goneToLs + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) @@ -1631,7 +1551,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING))) - sceneTransitions.value = goneToLs + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED) val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) @@ -1648,7 +1568,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val currentStatesReversed by collectValues(underTest.transition(Edge.create(null, LOCKSCREEN))) - sceneTransitions.value = goneToLs + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED) val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) @@ -1666,7 +1586,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED))) val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE))) - sceneTransitions.value = lsToGone + kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED) @@ -1683,7 +1603,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { testScope.runTest { val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE))) - sceneTransitions.value = goneToLs + kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt index 8b49d4361d48..601779f8fb02 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt @@ -54,8 +54,10 @@ import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider import com.android.systemui.settings.userTracker import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -103,9 +105,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { appName2, ) - private val underTest: EditModeViewModel by lazy { - kosmos.editModeViewModel - } + private val underTest: EditModeViewModel by lazy { kosmos.editModeViewModel } @Before fun setUp() { @@ -461,31 +461,87 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test - fun tileNotAvailable_notShowing() = with(kosmos) { - testScope.runTest { - val unavailableTile = "work" - qsTileFactory = FakeQSFactory { spec -> - FakeQSTile(userTracker.userId, spec != unavailableTile) + fun tileNotAvailable_notShowing() = + with(kosmos) { + testScope.runTest { + val unavailableTile = "work" + qsTileFactory = FakeQSFactory { spec -> + FakeQSTile(userTracker.userId, spec != unavailableTile) + } + tileAvailabilityInteractorsMap = + mapOf( + unavailableTile to + FakeTileAvailabilityInteractor( + emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } + ) + ) + val tiles by collectLastValue(underTest.tiles) + val currentTiles = + mutableListOf( + TileSpec.create("flashlight"), + TileSpec.create("airplane"), + TileSpec.create("alarm"), + ) + currentTilesInteractor.setTiles(currentTiles) + + underTest.startEditing() + + assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }) + .isTrue() } - tileAvailabilityInteractorsMap = mapOf( - unavailableTile to FakeTileAvailabilityInteractor( - emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun currentTiles_moveTileDown() = + with(kosmos) { + testScope.runTest { + val tiles by collectLastValue(underTest.tiles) + val currentTiles = + mutableListOf( + TileSpec.create("flashlight"), + TileSpec.create("airplane"), + TileSpec.create("internet"), + TileSpec.create("alarm"), ) - ) - val tiles by collectLastValue(underTest.tiles) - val currentTiles = + currentTilesInteractor.setTiles(currentTiles) + underTest.startEditing() + runCurrent() + + // Move flashlight tile to index 3 + underTest.addTile(TileSpec.create("flashlight"), 3) + + assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec }) + .containsExactly("airplane", "internet", "alarm", "flashlight") + .inOrder() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun currentTiles_moveTileUp() = + with(kosmos) { + testScope.runTest { + val tiles by collectLastValue(underTest.tiles) + val currentTiles = mutableListOf( - TileSpec.create("flashlight"), - TileSpec.create("airplane"), - TileSpec.create("alarm"), + TileSpec.create("flashlight"), + TileSpec.create("airplane"), + TileSpec.create("internet"), + TileSpec.create("alarm"), ) - currentTilesInteractor.setTiles(currentTiles) + currentTilesInteractor.setTiles(currentTiles) + underTest.startEditing() + runCurrent() - underTest.startEditing() + // Move alarm tile to index 0 + underTest.addTile(TileSpec.create("alarm"), 0) - assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue() + assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec }) + .containsExactly("alarm", "flashlight", "airplane", "internet") + .inOrder() + } } - } companion object { private val drawable1 = TestStubDrawable("drawable1") diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 5242fe33a281..3acb328e81af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -24,6 +24,7 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor @@ -62,6 +63,13 @@ class SceneInteractorTest : SysuiTestCase() { private val underTest = kosmos.sceneInteractor + init { + // Init lazy Fixtures. Accessing them once makes sure that the singletons are initialized + // and therefore starts to collect StateFlows eagerly (when there are any). + kosmos.deviceUnlockedInteractor + kosmos.keyguardEnabledInteractor + } + @Test fun allSceneKeys() { assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt index 17202639136b..ed30d59a94fa 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt @@ -397,7 +397,7 @@ constructor( Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"), Pair( keyguardTransitionInteractor - .isInTransitionToStateWhere(KeyguardState::deviceIsAsleepInState) + .isInTransitionWhere(toStatePredicate = KeyguardState::deviceIsAsleepInState) .isFalse(), "deviceNotTransitioningToAsleepState" ), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 5573f0d5f644..b44a8cf70328 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -56,6 +56,7 @@ class FromAlternateBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 2a9ee9fb8779..868c4629dbb3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -44,6 +44,7 @@ class FromAodTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index b423ed980060..3877e7ae1e55 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -44,6 +44,7 @@ class FromDozingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index 47aa02a0be52..117dbcfe52c8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -41,6 +41,7 @@ class FromDreamingLockscreenHostedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 7fa197c11e57..97006486b6f1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -47,6 +47,7 @@ class FromDreamingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index e516fa3c44bb..8041dd933143 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -50,6 +50,7 @@ constructor( private val glanceableHubTransitions: GlanceableHubTransitions, keyguardInteractor: KeyguardInteractor, override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 07a2b0424aba..b084824cd348 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -45,6 +45,7 @@ class FromGoneTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 35a2d58816b4..ff15a1bc34d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -57,6 +57,7 @@ class FromLockscreenTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @@ -128,7 +129,7 @@ constructor( keyguardInteractor.isAbleToDream .filterRelevantKeyguardState() .sampleCombine( - transitionInteractor.currentTransitionInfoInternal, + internalTransitionInteractor.currentTransitionInfoInternal, finishedKeyguardState, keyguardInteractor.isActiveDreamLockscreenHosted, ) @@ -185,7 +186,7 @@ constructor( shadeRepository.legacyShadeExpansion .sampleCombine( startedKeyguardTransitionStep, - transitionInteractor.currentTransitionInfoInternal, + internalTransitionInteractor.currentTransitionInfoInternal, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardDismissible, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 86d4cfb916ed..84ca6672d509 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -41,6 +41,7 @@ class FromOccludedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index f23b12f35977..f98ed15a21c6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -50,6 +50,7 @@ class FromPrimaryBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, + override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt new file mode 100644 index 000000000000..a51421c10309 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt @@ -0,0 +1,71 @@ +/* + * 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.keyguard.domain.interactor + +import android.annotation.FloatRange +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.keyguard.shared.model.TransitionState +import java.util.UUID +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** + * This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes + * functions to directly modify the transition state. + */ +@SysUISingleton +class InternalKeyguardTransitionInteractor +@Inject +constructor( + private val repository: KeyguardTransitionRepository, +) { + + /** + * The [TransitionInfo] of the most recent call to + * [KeyguardTransitionRepository.startTransition]. + * + * This should only be used by keyguard transition internals (From*TransitionInteractor and + * related classes). Other consumers of keyguard state in System UI should use + * [startedKeyguardState], [currentKeyguardState], and related flows. + * + * Keyguard internals use this to determine the most up-to-date KeyguardState that we've + * requested a transition to, even if the animator running the transition on the main thread has + * not yet emitted the STARTED TransitionStep. + * + * For example: if we're finished in GONE and press the power button twice very quickly, we may + * request a transition to AOD, but then receive the second power button press prior to the + * STARTED -> AOD transition step emitting. We still need the FromAodTransitionInteractor to + * request a transition from AOD -> LOCKSCREEN in response to the power press, even though the + * main thread animator hasn't emitted STARTED > AOD yet (which means [startedKeyguardState] is + * still GONE, which is not relevant to FromAodTransitionInteractor). In this case, the + * interactor can use this current transition info to determine that a STARTED -> AOD step + * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition + * which will subsequently cancel GONE -> AOD. + */ + internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> = + repository.currentTransitionInfoInternal + + suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info) + + fun updateTransition( + transitionId: UUID, + @FloatRange(from = 0.0, to = 1.0) value: Float, + state: TransitionState + ) = repository.updateTransition(transitionId, value, state) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt index 8d683f0d4375..66efde1c8358 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt @@ -49,6 +49,7 @@ constructor( val repository: KeyguardRepository, val biometricSettingsRepository: BiometricSettingsRepository, transitionInteractor: KeyguardTransitionInteractor, + internalTransitionInteractor: InternalKeyguardTransitionInteractor, ) { /** @@ -74,7 +75,7 @@ constructor( // Whenever the keyguard is disabled... .filter { enabled -> !enabled } .sampleCombine( - transitionInteractor.currentTransitionInfoInternal, + internalTransitionInteractor.currentTransitionInfoInternal, biometricSettingsRepository.isCurrentUserInLockdown ) .map { (_, transitionInfo, inLockdown) -> @@ -93,7 +94,7 @@ constructor( .filter { enabled -> !enabled } .sampleCombine( biometricSettingsRepository.isCurrentUserInLockdown, - transitionInteractor.currentTransitionInfoInternal, + internalTransitionInteractor.currentTransitionInfoInternal, ) .collect { (_, inLockdown, currentTransitionInfo) -> if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt index f3856710b1ed..41ccea735f5f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt @@ -53,6 +53,7 @@ constructor( private val repository: KeyguardOcclusionRepository, private val powerInteractor: PowerInteractor, private val transitionInteractor: KeyguardTransitionInteractor, + private val internalTransitionInteractor: InternalKeyguardTransitionInteractor, keyguardInteractor: KeyguardInteractor, deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, ) { @@ -78,7 +79,7 @@ constructor( // *_BOUNCER -> LOCKSCREEN. return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered && KeyguardState.deviceIsAsleepInState( - transitionInteractor.currentTransitionInfoInternal.value.to + internalTransitionInteractor.currentTransitionInfoInternal.value.to ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt index 5ad7762bb512..b3c9591f5668 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt @@ -40,6 +40,7 @@ constructor( val deviceEntryInteractor: DeviceEntryInteractor, val deviceProvisioningInteractor: DeviceProvisioningInteractor, val keyguardTransitionInteractor: KeyguardTransitionInteractor, + val internalTransitionInteractor: InternalKeyguardTransitionInteractor, val repository: KeyguardTransitionRepository, ) : CoreStartable { @@ -64,7 +65,7 @@ constructor( } if ( - keyguardTransitionInteractor.currentTransitionInfoInternal.value.from != + internalTransitionInteractor.currentTransitionInfoInternal.value.from != KeyguardState.OFF ) { Log.e( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 2d389aa93d76..f9bfaff80090 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.domain.interactor -import android.annotation.FloatRange import android.annotation.SuppressLint import android.util.Log import com.android.compose.animation.scene.SceneKey @@ -33,14 +32,12 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED -import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwise -import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -50,6 +47,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -75,7 +73,7 @@ constructor( private val fromAlternateBouncerTransitionInteractor: dagger.Lazy<FromAlternateBouncerTransitionInteractor>, private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>, - private val sceneInteractor: dagger.Lazy<SceneInteractor>, + private val sceneInteractor: SceneInteractor, ) { private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() @@ -194,7 +192,7 @@ constructor( fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) || - sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene) + sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene) } } else { flow @@ -228,7 +226,7 @@ constructor( stateWithoutSceneContainer: KeyguardState, ): Flow<Float> { return if (SceneContainerFlag.isEnabled) { - sceneInteractor.get().transitionProgress(scene) + sceneInteractor.transitionProgress(scene) } else { transitionValue(stateWithoutSceneContainer) } @@ -383,33 +381,14 @@ constructor( .distinctUntilChanged() .stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF) - /** - * The [TransitionInfo] of the most recent call to - * [KeyguardTransitionRepository.startTransition]. - * - * This should only be used by keyguard transition internals (From*TransitionInteractor and - * related classes). Other consumers of keyguard state in System UI should use - * [startedKeyguardState], [currentKeyguardState], and related flows. - * - * Keyguard internals use this to determine the most up-to-date KeyguardState that we've - * requested a transition to, even if the animator running the transition on the main thread has - * not yet emitted the STARTED TransitionStep. - * - * For example: if we're finished in GONE and press the power button twice very quickly, we may - * request a transition to AOD, but then receive the second power button press prior to the - * STARTED -> AOD transition step emitting. We still need the FromAodTransitionInteractor to - * request a transition from AOD -> LOCKSCREEN in response to the power press, even though the - * main thread animator hasn't emitted STARTED > AOD yet (which means [startedKeyguardState] is - * still GONE, which is not relevant to FromAodTransitionInteractor). In this case, the - * interactor can use this current transition info to determine that a STARTED -> AOD step - * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition - * which will subsequently cancel GONE -> AOD. - */ - internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> = - repository.currentTransitionInfoInternal - - /** Whether we've currently STARTED a transition and haven't yet FINISHED it. */ - val isInTransitionToAnyState = isInTransitionWhere({ true }, { true }) + val isInTransition = + combine( + isInTransitionWhere({ true }, { true }), + sceneInteractor.transitionState, + ) { isKeyguardTransitioning, sceneTransitionState -> + isKeyguardTransitioning || + (SceneContainerFlag.isEnabled && sceneTransitionState.isTransitioning()) + } /** * Called to start a transition that will ultimately dismiss the keyguard from the current @@ -422,7 +401,7 @@ constructor( // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") - when (val startedState = currentTransitionInfoInternal.value.to) { + when (val startedState = repository.currentTransitionInfoInternal.value.to) { LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard() PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer() ALTERNATE_BOUNCER -> @@ -448,7 +427,7 @@ constructor( fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> { return if (SceneContainerFlag.isEnabled) { if (edge.isSceneWildcardEdge()) { - sceneInteractor.get().transitionState.map { + sceneInteractor.transitionState.map { when (edge) { is Edge.StateToState -> throw IllegalStateException("Should not be reachable.") @@ -469,30 +448,6 @@ constructor( } /** - * Whether we're in a transition to a [KeyguardState] that matches the given predicate, but - * haven't yet completed it. - * - * If you only care about a single state, instead use the optimized [isInTransition]. - */ - fun isInTransitionToStateWhere( - stateMatcher: (KeyguardState) -> Boolean, - ): Flow<Boolean> { - return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher) - } - - /** - * Whether we're in a transition out of a [KeyguardState] that matches the given predicate, but - * haven't yet completed it. - * - * If you only care about a single state, instead use the optimized [isInTransition]. - */ - fun isInTransitionFromStateWhere( - stateMatcher: (KeyguardState) -> Boolean, - ): Flow<Boolean> { - return isInTransitionWhere(fromStatePredicate = stateMatcher, toStatePredicate = { true }) - } - - /** * Whether we're in a transition between two [KeyguardState]s that match the given predicates, * but haven't yet completed it. * @@ -500,27 +455,15 @@ constructor( * [isInTransition]. */ fun isInTransitionWhere( - fromStatePredicate: (KeyguardState) -> Boolean, - toStatePredicate: (KeyguardState) -> Boolean, - ): Flow<Boolean> { - return isInTransitionWhere { from, to -> fromStatePredicate(from) && toStatePredicate(to) } - } - - /** - * Whether we're in a transition between two [KeyguardState]s that match the given predicates, - * but haven't yet completed it. - * - * If you only care about a single state for both from and to, instead use the optimized - * [isInTransition]. - */ - private fun isInTransitionWhere( - fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean + fromStatePredicate: (KeyguardState) -> Boolean = { true }, + toStatePredicate: (KeyguardState) -> Boolean = { true }, ): Flow<Boolean> { return repository.transitions .filter { it.transitionState != TransitionState.CANCELED } .mapLatest { it.transitionState != TransitionState.FINISHED && - fromToStatePredicate(it.from, it.to) + fromStatePredicate(it.from) && + toStatePredicate(it.to) } .distinctUntilChanged() } @@ -532,9 +475,7 @@ constructor( fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> { return if (SceneContainerFlag.isEnabled) { - sceneInteractor - .get() - .transitionState + sceneInteractor.transitionState .map { it.isIdle(scene) || it.isTransitioning(from = scene) } .distinctUntilChanged() } else { @@ -560,14 +501,6 @@ constructor( return finishedKeyguardState.replayCache.last() } - suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info) - - fun updateTransition( - transitionId: UUID, - @FloatRange(from = 0.0, to = 1.0) value: Float, - state: TransitionState - ) = repository.updateTransition(transitionId, value, state) - companion object { private val TAG = KeyguardTransitionInteractor::class.simpleName } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index e14820714c9b..973e8989b82a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -57,6 +57,8 @@ sealed class TransitionInteractor( ) { val name = this::class.simpleName ?: "UnknownTransitionInteractor" abstract val transitionRepository: KeyguardTransitionRepository + abstract val internalTransitionInteractor: InternalKeyguardTransitionInteractor + abstract fun start() /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because @@ -79,14 +81,14 @@ sealed class TransitionInteractor( // a bugreport. ownerReason: String = "", ): UUID? { - if (fromState != transitionInteractor.currentTransitionInfoInternal.value.to) { + if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) { Log.e( name, "Ignoring startTransition: This interactor asked to transition from " + "$fromState -> $toState, but we last transitioned to " + - "${transitionInteractor.currentTransitionInfoInternal.value.to}, not " + - "$fromState. This should never happen - check currentTransitionInfoInternal " + - "or use filterRelevantKeyguardState before starting transitions." + "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" + + " $fromState. This should never happen - check currentTransitionInfoInternal" + + " or use filterRelevantKeyguardState before starting transitions." ) if (fromState == transitionInteractor.finishedKeyguardState.replayCache.last()) { @@ -238,12 +240,11 @@ sealed class TransitionInteractor( * Whether we're in the KeyguardState relevant to this From*TransitionInteractor (which we know * from [fromState]). * - * This uses [KeyguardTransitionInteractor.currentTransitionInfoInternal], which is more up to - * date than [startedKeyguardState] as it does not wait for the emission of the first STARTED - * step. + * This uses [currentTransitionInfoInternal], which is more up to date than + * [startedKeyguardState] as it does not wait for the emission of the first STARTED step. */ fun inOrTransitioningToRelevantKeyguardState(): Boolean { - return transitionInteractor.currentTransitionInfoInternal.value.to == fromState + return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index 069f65b4efa6..3355ffd83138 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -129,7 +129,7 @@ constructor( } } } else { - transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition -> + transitionInteractor.isInTransition.flatMapLatest { isInTransition -> if (!isInTransition) { defaultSurfaceBehindVisibility } else { @@ -206,11 +206,11 @@ constructor( transitionInteractor.currentKeyguardState .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair) .map { (currentState, startedWithPrev) -> - val startedFromStep = startedWithPrev?.previousValue - val startedStep = startedWithPrev?.newValue + val startedFromStep = startedWithPrev.previousValue + val startedStep = startedWithPrev.newValue val returningToGoneAfterCancellation = - startedStep?.to == KeyguardState.GONE && - startedFromStep?.transitionState == TransitionState.CANCELED && + startedStep.to == KeyguardState.GONE && + startedFromStep.transitionState == TransitionState.CANCELED && startedFromStep.from == KeyguardState.GONE if (!returningToGoneAfterCancellation) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index 9b3ba7d8feb0..324811443e9d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository.Companion.DEFAULT_STATE +import com.android.systemui.keyguard.domain.interactor.InternalKeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED @@ -67,7 +68,8 @@ import kotlinx.coroutines.launch class LockscreenSceneTransitionInteractor @Inject constructor( - val transitionInteractor: KeyguardTransitionInteractor, + private val transitionInteractor: KeyguardTransitionInteractor, + private val internalTransitionInteractor: InternalKeyguardTransitionInteractor, @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val repository: LockscreenSceneTransitionRepository, @@ -123,7 +125,7 @@ constructor( } private fun finishCurrentTransition() { - transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED) + internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED) resetTransitionData() } @@ -131,13 +133,13 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = transitionInteractor.currentTransitionInfoInternal.value.to, + from = internalTransitionInteractor.currentTransitionInfoInternal.value.to, to = state, animator = null, modeOnCanceled = TransitionModeOnCanceled.REVERSE ) - currentTransitionId = transitionInteractor.startTransition(newTransition) - transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED) + currentTransitionId = internalTransitionInteractor.startTransition(newTransition) + internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED) resetTransitionData() } @@ -150,7 +152,8 @@ constructor( private suspend fun handleTransition(transition: ObservableTransitionState.Transition) { if (transition.fromScene == Scenes.Lockscreen) { if (currentTransitionId != null) { - val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to + val currentToState = + internalTransitionInteractor.currentTransitionInfoInternal.value.to if (currentToState == UNDEFINED) { transitionKtfTo(transitionInteractor.getStartedFromState()) } @@ -201,7 +204,7 @@ constructor( } private suspend fun startTransitionFromLockscreen() { - val currentState = transitionInteractor.currentTransitionInfoInternal.value.to + val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, @@ -217,12 +220,12 @@ constructor( if (currentTransitionId != null) { resetTransitionData() } - currentTransitionId = transitionInteractor.startTransition(transitionInfo) + currentTransitionId = internalTransitionInteractor.startTransition(transitionInfo) } private fun updateProgress(progress: Float) { if (currentTransitionId == null) return - transitionInteractor.updateTransition( + internalTransitionInteractor.updateTransition( currentTransitionId!!, progress.coerceIn(0f, 1f), RUNNING diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt index 19b8c66983f9..62bfc72f07f2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt @@ -27,6 +27,8 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec +import javax.inject.Inject +import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -37,22 +39,20 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import javax.inject.Inject -import javax.inject.Named @SysUISingleton @OptIn(ExperimentalCoroutinesApi::class) class EditModeViewModel @Inject constructor( - private val editTilesListInteractor: EditTilesListInteractor, - private val currentTilesInteractor: CurrentTilesInteractor, - private val tilesAvailabilityInteractor: TilesAvailabilityInteractor, - private val minTilesInteractor: MinimumTilesInteractor, - @Named("Default") private val defaultGridLayout: GridLayout, - @Application private val applicationScope: CoroutineScope, - gridLayoutTypeInteractor: GridLayoutTypeInteractor, - gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>, + private val editTilesListInteractor: EditTilesListInteractor, + private val currentTilesInteractor: CurrentTilesInteractor, + private val tilesAvailabilityInteractor: TilesAvailabilityInteractor, + private val minTilesInteractor: MinimumTilesInteractor, + @Named("Default") private val defaultGridLayout: GridLayout, + @Application private val applicationScope: CoroutineScope, + gridLayoutTypeInteractor: GridLayoutTypeInteractor, + gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>, ) { private val _isEditing = MutableStateFlow(false) @@ -93,10 +93,12 @@ constructor( val editTilesData = editTilesListInteractor.getTilesToEdit() // Query only the non current platform tiles, as any current tile is clearly // available - val unavailable = tilesAvailabilityInteractor.getUnavailableTiles( - editTilesData.stockTiles.map { it.tileSpec } - .minus(currentTilesInteractor.currentTilesSpecs.toSet()) - ) + val unavailable = + tilesAvailabilityInteractor.getUnavailableTiles( + editTilesData.stockTiles + .map { it.tileSpec } + .minus(currentTilesInteractor.currentTilesSpecs.toSet()) + ) currentTilesInteractor.currentTiles.map { tiles -> val currentSpecs = tiles.map { it.spec } val canRemoveTiles = currentSpecs.size > minimumTiles @@ -106,28 +108,28 @@ constructor( val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs } (currentTiles + nonCurrentTiles) - .filterNot { it.tileSpec in unavailable } - .map { - val current = it.tileSpec in currentSpecs - val availableActions = buildSet { - if (current) { - add(AvailableEditActions.MOVE) - if (canRemoveTiles) { - add(AvailableEditActions.REMOVE) - } - } else { - add(AvailableEditActions.ADD) + .filterNot { it.tileSpec in unavailable } + .map { + val current = it.tileSpec in currentSpecs + val availableActions = buildSet { + if (current) { + add(AvailableEditActions.MOVE) + if (canRemoveTiles) { + add(AvailableEditActions.REMOVE) } + } else { + add(AvailableEditActions.ADD) } - EditTileViewModel( - it.tileSpec, - it.icon, - it.label, - it.appName, - current, - availableActions - ) } + EditTileViewModel( + it.tileSpec, + it.icon, + it.label, + it.appName, + current, + availableActions + ) + } } } else { emptyFlow() @@ -144,13 +146,16 @@ constructor( _isEditing.value = false } - /** Immediately moves [tileSpec] to [position]. */ - fun moveTile(tileSpec: TileSpec, position: Int) { - throw NotImplementedError("This is not supported yet") - } - - /** Immediately adds [tileSpec] to the current tiles at [position]. */ + /** + * Immediately adds [tileSpec] to the current tiles at [position]. If the [tileSpec] was already + * present, it will be moved to the new position. + */ fun addTile(tileSpec: TileSpec, position: Int = POSITION_AT_END) { + // Removing tile if it's already present to insert it at the new index. + if (currentTilesInteractor.currentTilesSpecs.contains(tileSpec)) { + removeTile(tileSpec) + } + currentTilesInteractor.addTile(tileSpec, position) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index ef011945b5c8..5885193aa017 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -28,6 +28,7 @@ import com.android.systemui.scene.domain.resolver.SceneResolver import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.getValue import com.android.systemui.util.kotlin.pairwiseBy import dagger.Lazy import javax.inject.Inject @@ -60,8 +61,8 @@ constructor( private val repository: SceneContainerRepository, private val logger: SceneLogger, private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>, - private val deviceUnlockedInteractor: DeviceUnlockedInteractor, - private val keyguardEnabledInteractor: KeyguardEnabledInteractor, + private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, + private val keyguardEnabledInteractor: Lazy<KeyguardEnabledInteractor>, ) { interface OnSceneAboutToChangeListener { @@ -387,8 +388,8 @@ constructor( val isChangeAllowed = to != Scenes.Gone || inMidTransitionFromGone || - deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked || - !keyguardEnabledInteractor.isKeyguardEnabled.value + deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked || + !keyguardEnabledInteractor.get().isKeyguardEnabled.value check(isChangeAllowed) { "Cannot change to the Gone scene while the device is locked and not currently" + " transitioning from Gone. Current transition state is ${transitionState.value}." + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt index ca7308161e14..ef96f43ec17e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -90,17 +91,22 @@ constructor( /** Occlusion state to apply whenever a keyguard transition is FINISHED. */ private val occlusionStateFromFinishedStep = - keyguardTransitionInteractor.finishedKeyguardTransitionStep - .sample(keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, ::Pair) - .map { (finishedStep, showWhenLockedOnTop) -> + combine( + keyguardTransitionInteractor.isFinishedIn( + Scenes.Gone, + stateWithoutSceneContainer = KeyguardState.GONE + ), + keyguardTransitionInteractor.isFinishedIn(KeyguardState.OCCLUDED), + keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, + ::Triple + ) + .map { (isOnGone, isOnOccluded, showWhenLockedOnTop) -> // If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to // remain occluded if a SHOW_WHEN_LOCKED activity is on the top of the task stack, // and we're in any state other than GONE. This is necessary, for example, when we // transition from OCCLUDED to a bouncer state. Otherwise, we should not be // occluded. - val occluded = - finishedStep.to == KeyguardState.OCCLUDED || - (showWhenLockedOnTop && finishedStep.to != KeyguardState.GONE) + val occluded = isOnOccluded || (showWhenLockedOnTop && !isOnGone) OccludedState(occluded = occluded, animate = false) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 1c57346b18e0..900201f54bd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -372,7 +372,7 @@ constructor( paddingTopDimen, interactor.topPosition .sampleCombine( - keyguardTransitionInteractor.isInTransitionToAnyState, + keyguardTransitionInteractor.isInTransition, shadeInteractor.qsExpansion, ) .onStart { emit(Triple(0f, false, 0f)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index e7ca091aaf4c..b80d1a472a72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -419,7 +419,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl( new ShadeAnimationRepository(), mShadeRepository); mPowerInteractor = keyguardInteractorDeps.getPowerInteractor(); - when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn( + when(mKeyguardTransitionInteractor.isInTransitionWhere(any(), any())).thenReturn( MutableStateFlow(false)); when(mKeyguardTransitionInteractor.isInTransition(any(), any())) .thenReturn(emptyFlow()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 0cb28cb4a267..68e17c1b2d73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -316,6 +316,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void setBatteryListening_true_callbackAdded() { mController.setBatteryListening(true); @@ -323,6 +324,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void setBatteryListening_false_callbackRemoved() { // First set to true so that we know setting to false is a change in state. mController.setBatteryListening(true); @@ -333,6 +335,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void setBatteryListening_trueThenTrue_callbackAddedOnce() { mController.setBatteryListening(true); mController.setBatteryListening(true); @@ -372,6 +375,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_alphaAndVisibilityGiven_viewUpdated() { // Verify the initial values so we know the method triggers changes. assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(1f); @@ -386,6 +390,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_paramVisibleButIsDisabled_viewIsInvisible() { mController.onViewAttached(); setDisableSystemIcons(true); @@ -397,6 +402,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_notKeyguardState_nothingUpdated() { mController.onViewAttached(); updateStateToNotKeyguard(); @@ -409,6 +415,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -424,6 +431,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_bypassNotEnabled_viewShown() { mController.onViewAttached(); updateStateToKeyguard(); @@ -438,6 +446,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_shouldNotListenForFace_viewShown() { mController.onViewAttached(); updateStateToKeyguard(); @@ -452,6 +461,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_panelExpandedHeightZero_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -464,6 +474,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_dragProgressOne_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -476,6 +487,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_disableSystemInfoFalse_viewShown() { mController.onViewAttached(); updateStateToKeyguard(); @@ -487,6 +499,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_disableSystemInfoTrue_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -498,6 +511,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_disableSystemIconsFalse_viewShown() { mController.onViewAttached(); updateStateToKeyguard(); @@ -509,6 +523,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateViewState_disableSystemIconsTrue_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -608,6 +623,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void setAlpha_explicitAlpha_setsExplicitAlpha() { mController.onViewAttached(); updateStateToKeyguard(); @@ -618,6 +634,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void setAlpha_explicitAlpha_thenMinusOneAlpha_setsAlphaBasedOnDefaultCriteria() { mController.onViewAttached(); updateStateToKeyguard(); @@ -632,6 +649,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized. @Test + @DisableSceneContainer public void updateForHeadsUp_headsUpShouldBeVisible_viewHidden() { mController.onViewAttached(); updateStateToKeyguard(); @@ -644,6 +662,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() { mController.onViewAttached(); updateStateToKeyguard(); @@ -733,6 +752,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void animateKeyguardStatusBarIn_isDisabled_viewStillHidden() { mController.onViewAttached(); updateStateToKeyguard(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt index 78a419f92495..ce317d43e988 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt @@ -32,6 +32,7 @@ val Kosmos.fromAlternateBouncerTransitionInteractor by FromAlternateBouncerTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt index 42af25ed51a7..ae138c8f930b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt @@ -29,6 +29,7 @@ val Kosmos.fromAodTransitionInteractor by FromAodTransitionInteractor( transitionRepository = fakeKeyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt index edf77a007efd..e7e007fd79fa 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt @@ -30,6 +30,7 @@ var Kosmos.fromDozingTransitionInteractor by FromDozingTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt index f7a9d59eac26..7ebef10b12c6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt @@ -28,6 +28,7 @@ var Kosmos.fromDreamingLockscreenHostedTransitionInteractor by FromDreamingLockscreenHostedTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt index 135644cfac3e..a9be06d6387f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt @@ -28,6 +28,7 @@ var Kosmos.fromDreamingTransitionInteractor by FromDreamingTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt index 1695327d75bc..67846589cb89 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt @@ -28,6 +28,7 @@ var Kosmos.fromGlanceableHubTransitionInteractor by FromGlanceableHubTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt index 4039ee6ea904..317294f3c884 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt @@ -31,6 +31,7 @@ val Kosmos.fromGoneTransitionInteractor by FromGoneTransitionInteractor( transitionRepository = fakeKeyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index 28bd43982eba..4131145857e0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -29,6 +29,7 @@ var Kosmos.fromLockscreenTransitionInteractor by FromLockscreenTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt index fc740a180dc4..c2169456eac5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt @@ -29,6 +29,7 @@ val Kosmos.fromOccludedTransitionInteractor by FromOccludedTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index d72b9c10d3d8..42ee15216590 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -31,6 +31,7 @@ var Kosmos.fromPrimaryBouncerTransitionInteractor by FromPrimaryBouncerTransitionInteractor( transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..017a9ecd7f9c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt @@ -0,0 +1,27 @@ +/* + * 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.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos + +val Kosmos.internalKeyguardTransitionInteractor by + Kosmos.Fixture { + InternalKeyguardTransitionInteractor( + repository = keyguardTransitionRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt index 0667a6b9536a..c6b5ed0b608f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt @@ -28,5 +28,6 @@ val Kosmos.keyguardEnabledInteractor by keyguardRepository, biometricSettingsRepository, keyguardTransitionInteractor, + internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt index 7d8d33f7dd11..5836902ad54d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt @@ -30,5 +30,6 @@ val Kosmos.keyguardTransitionBootInteractor: KeyguardTransitionBootInteractor by deviceProvisioningInteractor = deviceProvisioningInteractor, keyguardTransitionInteractor = keyguardTransitionInteractor, repository = keyguardTransitionRepository, + internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt index c90642d93939..c5da10e59369 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -33,6 +33,6 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by fromAodTransitionInteractor = { fromAodTransitionInteractor }, fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor }, fromDozingTransitionInteractor = { fromDozingTransitionInteractor }, - sceneInteractor = { sceneInteractor } + sceneInteractor = sceneInteractor ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index 3c1f7b1b2394..e50e04499168 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor.scenetransition import com.android.systemui.keyguard.data.repository.lockscreenSceneTransitionRepository +import com.android.systemui.keyguard.domain.interactor.internalKeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -29,5 +30,6 @@ var Kosmos.lockscreenSceneTransitionInteractor by applicationScope = applicationCoroutineScope, sceneInteractor = sceneInteractor, repository = lockscreenSceneTransitionRepository, + internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index 0921eb9e83d3..ae8b411a4b95 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -31,7 +31,7 @@ val Kosmos.sceneInteractor by repository = sceneContainerRepository, logger = sceneLogger, sceneFamilyResolvers = { sceneFamilyResolvers }, - deviceUnlockedInteractor = deviceUnlockedInteractor, - keyguardEnabledInteractor = keyguardEnabledInteractor, + deviceUnlockedInteractor = { deviceUnlockedInteractor }, + keyguardEnabledInteractor = { keyguardEnabledInteractor }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt index a90a9ffc8a33..65016c3eefb8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.domain.interactor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor +import com.android.systemui.keyguard.domain.interactor.internalKeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos @@ -34,5 +35,6 @@ val Kosmos.keyguardOcclusionInteractor by transitionInteractor = keyguardTransitionInteractor, keyguardInteractor = keyguardInteractor, deviceUnlockedInteractor = { deviceUnlockedInteractor }, + internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index dfd148dfc0ec..a4026eb151b9 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -241,12 +241,14 @@ public class RescueParty { * opportunity to reset any settings depending on our rescue level. */ public static void onSettingsProviderPublished(Context context) { - handleNativeRescuePartyResets(); - ContentResolver contentResolver = context.getContentResolver(); - DeviceConfig.setMonitorCallback( - contentResolver, - Executors.newSingleThreadExecutor(), - new RescuePartyMonitorCallback(context)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + handleNativeRescuePartyResets(); + ContentResolver contentResolver = context.getContentResolver(); + DeviceConfig.setMonitorCallback( + contentResolver, + Executors.newSingleThreadExecutor(), + new RescuePartyMonitorCallback(context)); + } } @@ -256,75 +258,81 @@ public class RescueParty { * on modules of newer versions. */ public static void resetDeviceConfigForPackages(List<String> packageNames) { - if (packageNames == null) { - return; - } - Set<String> namespacesToReset = new ArraySet<String>(); - Iterator<String> it = packageNames.iterator(); - RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); - // Get runtime package to namespace mapping if created. - if (rescuePartyObserver != null) { - while (it.hasNext()) { - String packageName = it.next(); - Set<String> runtimeAffectedNamespaces = - rescuePartyObserver.getAffectedNamespaceSet(packageName); - if (runtimeAffectedNamespaces != null) { - namespacesToReset.addAll(runtimeAffectedNamespaces); + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (packageNames == null) { + return; + } + Set<String> namespacesToReset = new ArraySet<String>(); + Iterator<String> it = packageNames.iterator(); + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); + // Get runtime package to namespace mapping if created. + if (rescuePartyObserver != null) { + while (it.hasNext()) { + String packageName = it.next(); + Set<String> runtimeAffectedNamespaces = + rescuePartyObserver.getAffectedNamespaceSet(packageName); + if (runtimeAffectedNamespaces != null) { + namespacesToReset.addAll(runtimeAffectedNamespaces); + } } } - } - // Get preset package to namespace mapping if created. - Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( - packageNames); - if (presetAffectedNamespaces != null) { - namespacesToReset.addAll(presetAffectedNamespaces); - } + // Get preset package to namespace mapping if created. + Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( + packageNames); + if (presetAffectedNamespaces != null) { + namespacesToReset.addAll(presetAffectedNamespaces); + } - // Clear flags under the namespaces mapped to these packages. - // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. - Iterator<String> namespaceIt = namespacesToReset.iterator(); - while (namespaceIt.hasNext()) { - String namespaceToReset = namespaceIt.next(); - Properties properties = new Properties.Builder(namespaceToReset).build(); - try { - if (!DeviceConfig.setProperties(properties)) { - logCriticalInfo(Log.ERROR, "Failed to clear properties under " + // Clear flags under the namespaces mapped to these packages. + // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. + Iterator<String> namespaceIt = namespacesToReset.iterator(); + while (namespaceIt.hasNext()) { + String namespaceToReset = namespaceIt.next(); + Properties properties = new Properties.Builder(namespaceToReset).build(); + try { + if (!DeviceConfig.setProperties(properties)) { + logCriticalInfo(Log.ERROR, "Failed to clear properties under " + namespaceToReset + ". Running `device_config get_sync_disabled_for_tests` will confirm" + " if config-bulk-update is enabled."); + } + } catch (DeviceConfig.BadConfigException exception) { + logCriticalInfo(Log.WARN, "namespace " + namespaceToReset + + " is already banned, skip reset."); } - } catch (DeviceConfig.BadConfigException exception) { - logCriticalInfo(Log.WARN, "namespace " + namespaceToReset - + " is already banned, skip reset."); } } } private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { Set<String> resultSet = new ArraySet<String>(); - try { - String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, - NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); - String[] mappingEntries = flagVal.split(","); - for (int i = 0; i < mappingEntries.length; i++) { - if (TextUtils.isEmpty(mappingEntries[i])) { - continue; - } - String[] splittedEntry = mappingEntries[i].split(":"); - if (splittedEntry.length != 2) { - throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); - } - String namespace = splittedEntry[0]; - String packageName = splittedEntry[1]; + if (!Flags.deprecateFlagsAndSettingsResets()) { + try { + String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, + NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); + String[] mappingEntries = flagVal.split(","); + for (int i = 0; i < mappingEntries.length; i++) { + if (TextUtils.isEmpty(mappingEntries[i])) { + continue; + } + String[] splitEntry = mappingEntries[i].split(":"); + if (splitEntry.length != 2) { + throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); + } + String namespace = splitEntry[0]; + String packageName = splitEntry[1]; - if (packageNames.contains(packageName)) { - resultSet.add(namespace); + if (packageNames.contains(packageName)) { + resultSet.add(namespace); + } } + } catch (Exception e) { + resultSet.clear(); + Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); + } finally { + return resultSet; } - } catch (Exception e) { - resultSet.clear(); - Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); - } finally { + } else { return resultSet; } } @@ -342,43 +350,54 @@ public class RescueParty { } public void onNamespaceUpdate(@NonNull String updatedNamespace) { - startObservingPackages(mContext, updatedNamespace); + if (!Flags.deprecateFlagsAndSettingsResets()) { + startObservingPackages(mContext, updatedNamespace); + } } public void onDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { - RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess( - callingPackage, - namespace); + + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess( + callingPackage, + namespace); + } } } private static void startObservingPackages(Context context, @NonNull String updatedNamespace) { - RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); - Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace); - if (callingPackages == null) { - return; + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); + Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet( + updatedNamespace); + if (callingPackages == null) { + return; + } + List<String> callingPackageList = new ArrayList<>(); + callingPackageList.addAll(callingPackages); + Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " + + updatedNamespace); + PackageWatchdog.getInstance(context).startObservingHealth( + rescuePartyObserver, + callingPackageList, + DEFAULT_OBSERVING_DURATION_MS); } - List<String> callingPackageList = new ArrayList<>(); - callingPackageList.addAll(callingPackages); - Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " - + updatedNamespace); - PackageWatchdog.getInstance(context).startObservingHealth( - rescuePartyObserver, - callingPackageList, - DEFAULT_OBSERVING_DURATION_MS); } private static void handleNativeRescuePartyResets() { - if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { - String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); - for (int i = 0; i < resetNativeCategories.length; i++) { - // Don't let RescueParty reset the namespace for RescueParty switches. - if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { - continue; + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { + String[] resetNativeCategories = + SettingsToPropertiesMapper.getResetNativeCategories(); + for (int i = 0; i < resetNativeCategories.length; i++) { + // Don't let RescueParty reset the namespace for RescueParty switches. + if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { + continue; + } + DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, + resetNativeCategories[i]); } - DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, - resetNativeCategories[i]); } } } @@ -400,6 +419,13 @@ public class RescueParty { } } + private static int getMaxRescueLevel() { + if (!SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { + return Level.factoryReset(); + } + return Level.reboot(); + } + /** * Get the rescue level to perform if this is the n-th attempt at mitigating failure. * @@ -409,19 +435,30 @@ public class RescueParty { * @return the rescue level for the n-th mitigation attempt. */ private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) { - if (mitigationCount == 1) { - return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; - } else if (mitigationCount == 2) { - return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; - } else if (mitigationCount == 3) { - return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; - } else if (mitigationCount == 4) { - return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT); - } else if (mitigationCount >= 5) { - return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET); + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (mitigationCount == 1) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; + } else if (mitigationCount == 2) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; + } else if (mitigationCount == 3) { + return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; + } else if (mitigationCount == 4) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT); + } else if (mitigationCount >= 5) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } } else { - Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); - return LEVEL_NONE; + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } } } @@ -451,13 +488,13 @@ public class RescueParty { return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_WARM_REBOOT); } else if (mitigationCount == 4) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); } else if (mitigationCount == 5) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES); + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES); } else if (mitigationCount == 6) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS); + RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS); } else if (mitigationCount >= 7) { return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_FACTORY_RESET); } else { @@ -465,6 +502,22 @@ public class RescueParty { } } + /** + * Get the rescue level to perform if this is the n-th attempt at mitigating failure. + * + * @param mitigationCount the mitigation attempt number (1 = first attempt etc.). + * @return the rescue level for the n-th mitigation attempt. + */ + private static @RescueLevels int getRescueLevel(int mitigationCount) { + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + return Level.none(); + } + } + private static void executeRescueLevel(Context context, @Nullable String failedPackage, int level) { Slog.w(TAG, "Attempting rescue level " + levelToString(level)); @@ -537,13 +590,22 @@ public class RescueParty { executeWarmReboot(context, level, failedPackage); break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, + level); + } break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, + level); + } break; case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, + level); + } break; case RESCUE_LEVEL_FACTORY_RESET: // Before the completion of Reboot, if any crash happens then PackageWatchdog @@ -560,6 +622,12 @@ public class RescueParty { private static void executeWarmReboot(Context context, int level, @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } + // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. setRebootProperty(true); @@ -579,6 +647,11 @@ public class RescueParty { private static void executeFactoryReset(Context context, int level, @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } setFactoryResetProperty(true); long now = System.currentTimeMillis(); setLastFactoryResetTimeMs(now); @@ -655,30 +728,32 @@ public class RescueParty { private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { - // No need to reset Settings again if they are already reset in the current level once. - if (getMaxRescueLevelAttempted() >= level) { - return; - } - setMaxRescueLevelAttempted(level); - // Try our best to reset all settings possible, and once finished - // rethrow any exception that we encountered - Exception res = null; - final ContentResolver resolver = context.getContentResolver(); - try { - Settings.Global.resetToDefaultsAsUser(resolver, null, mode, - UserHandle.SYSTEM.getIdentifier()); - } catch (Exception e) { - res = new RuntimeException("Failed to reset global settings", e); - } - for (int userId : getAllUserIds()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + // No need to reset Settings again if they are already reset in the current level once. + if (getMaxRescueLevelAttempted() >= level) { + return; + } + setMaxRescueLevelAttempted(level); + // Try our best to reset all settings possible, and once finished + // rethrow any exception that we encountered + Exception res = null; + final ContentResolver resolver = context.getContentResolver(); try { - Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); + Settings.Global.resetToDefaultsAsUser(resolver, null, mode, + UserHandle.SYSTEM.getIdentifier()); } catch (Exception e) { - res = new RuntimeException("Failed to reset secure settings for " + userId, e); + res = new RuntimeException("Failed to reset global settings", e); + } + for (int userId : getAllUserIds()) { + try { + Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); + } catch (Exception e) { + res = new RuntimeException("Failed to reset secure settings for " + userId, e); + } + } + if (res != null) { + throw res; } - } - if (res != null) { - throw res; } } @@ -731,11 +806,15 @@ public class RescueParty { if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { if (Flags.recoverabilityDetection()) { - return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage), failedPackage)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + mayPerformReboot(failedPackage), failedPackage)); + } else { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } } else { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage))); + mayPerformReboot(failedPackage))); } } else { return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; @@ -750,10 +829,17 @@ public class RescueParty { } if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { - final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage), failedPackage) - : getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage)); + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage), + failedPackage); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage)); + } executeRescueLevel(mContext, failedPackage == null ? null : failedPackage.getPackageName(), level); return true; @@ -787,8 +873,12 @@ public class RescueParty { return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } if (Flags.recoverabilityDetection()) { - return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - true, /*failedPackage=*/ null)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + true, /*failedPackage=*/ null)); + } else { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } } else { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); } @@ -800,9 +890,17 @@ public class RescueParty { return false; } boolean mayPerformReboot = !shouldThrottleReboot(); - final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount, - mayPerformReboot, /*failedPackage=*/ null) - : getRescueLevel(mitigationCount, mayPerformReboot); + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot, + /*failedPackage=*/ null); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot); + } executeRescueLevel(mContext, /*failedPackage=*/ null, level); return true; } @@ -828,18 +926,6 @@ public class RescueParty { return isPersistentSystemApp(failingPackage.getPackageName()); } - /** - * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset. - * Will return {@code false} if a factory reset was already offered recently. - */ - private boolean shouldThrottleReboot() { - Long lastResetTime = getLastFactoryResetTimeMs(); - long now = System.currentTimeMillis(); - long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); - return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); - } - private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { @@ -852,20 +938,22 @@ public class RescueParty { private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { - // Record it in calling packages to namespace map - Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); - if (namespaceSet == null) { - namespaceSet = new ArraySet<>(); - mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); - } - namespaceSet.add(namespace); - // Record it in namespace to calling packages map - Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); - if (callingPackageSet == null) { - callingPackageSet = new ArraySet<>(); + if (!Flags.deprecateFlagsAndSettingsResets()) { + // Record it in calling packages to namespace map + Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); + if (namespaceSet == null) { + namespaceSet = new ArraySet<>(); + mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); + } + namespaceSet.add(namespace); + // Record it in namespace to calling packages map + Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); + if (callingPackageSet == null) { + callingPackageSet = new ArraySet<>(); + } + callingPackageSet.add(callingPackage); + mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); } - callingPackageSet.add(callingPackage); - mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); } private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) { @@ -881,6 +969,18 @@ public class RescueParty { } } + /** + * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset. + * Will return {@code false} if a factory reset was already offered recently. + */ + private static boolean shouldThrottleReboot() { + Long lastResetTime = getLastFactoryResetTimeMs(); + long now = System.currentTimeMillis(); + long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, + DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); + return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); + } + private static int[] getAllUserIds() { int systemUserId = UserHandle.SYSTEM.getIdentifier(); int[] userIds = { systemUserId }; @@ -919,6 +1019,22 @@ public class RescueParty { } } + private static class Level { + static int none() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_NONE : LEVEL_NONE; + } + + static int reboot() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_WARM_REBOOT : LEVEL_WARM_REBOOT; + } + + static int factoryReset() { + return Flags.recoverabilityDetection() + ? RESCUE_LEVEL_FACTORY_RESET + : LEVEL_FACTORY_RESET; + } + } + private static String levelToString(int level) { if (Flags.recoverabilityDetection()) { switch (level) { diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 8f39ffb3f53c..685ab3a9424f 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -16,6 +16,8 @@ package com.android.server.rollback; +import static android.crashrecovery.flags.Flags.deprecateFlagsAndSettingsResets; + import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure; import android.Manifest; @@ -623,8 +625,10 @@ class Rollback { parentSession.addChildSessionId(sessionId); } - // Clear flags. - RescueParty.resetDeviceConfigForPackages(packageNames); + if (!deprecateFlagsAndSettingsResets()) { + // Clear flags. + RescueParty.resetDeviceConfigForPackages(packageNames); + } Consumer<Intent> onResult = result -> { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 35a77022737f..f839ed6a5612 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1694,7 +1694,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // ActivityRecord#canShowWindows() may reject to show its window. The visibility also // needs to be updated for STATE_ABORT. commitVisibleActivities(transaction); - commitVisibleWallpapers(); + commitVisibleWallpapers(transaction); if (mTransactionCompletedListeners != null) { for (int i = 0; i < mTransactionCompletedListeners.size(); i++) { @@ -2128,7 +2128,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** * Reset waitingToshow for all wallpapers, and commit the visibility of the visible ones */ - private void commitVisibleWallpapers() { + private void commitVisibleWallpapers(SurfaceControl.Transaction t) { boolean showWallpaper = shouldWallpaperBeVisible(); for (int i = mParticipants.size() - 1; i >= 0; --i) { final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken(); @@ -2136,6 +2136,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) { wallpaper.commitVisibility(showWallpaper); } + if (showWallpaper && Flags.ensureWallpaperInTransitions() + && wallpaper.isVisibleRequested() + && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) { + // If on a rotation leash, we need to explicitly show the wallpaper surface + // because shell only gets the leash and we don't allow non-transition logic + // to touch the surfaces until the transition is over. + t.show(wallpaper.getSurfaceControl()); + } } } } @@ -2837,6 +2845,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Use parent rotation because shell doesn't know the surface is rotated. endRotation = parent.getWindowConfiguration().getRotation(); } + } else if (isWallpaper(target) && Flags.ensureWallpaperInTransitions() + && target.getRelativeDisplayRotation() != 0 + && !target.mTransitionController.useShellTransitionsRotation()) { + // If the wallpaper is "fixed-rotated", shell is unaware of this, so use the + // "as-if-not-rotating" bounds and rotation + change.setEndAbsBounds(parent.getBounds()); + endRotation = parent.getWindowConfiguration().getRotation(); } else { change.setEndAbsBounds(bounds); } diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 337789950a32..f2acbc31b008 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -27,7 +27,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN; import static com.android.server.RescueParty.LEVEL_FACTORY_RESET; -import static com.android.server.RescueParty.RESCUE_LEVEL_FACTORY_RESET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -36,7 +35,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import android.content.ContentResolver; import android.content.Context; @@ -47,6 +45,9 @@ import android.crashrecovery.flags.Flags; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.provider.Settings; @@ -61,6 +62,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -77,10 +81,14 @@ import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; + /** * Test RescueParty. */ +@RunWith(Parameterized.class) public class RescuePartyTest { + @Rule + public SetFlagsRule mSetFlagsRule; private static final long CURRENT_NETWORK_TIME_MILLIS = 0L; private static final String FAKE_NATIVE_NAMESPACE1 = "native1"; private static final String FAKE_NATIVE_NAMESPACE2 = "native2"; @@ -103,9 +111,6 @@ public class RescuePartyTest { private static final String PROP_DISABLE_FACTORY_RESET_FLAG = "persist.device_config.configuration.disable_rescue_party_factory_reset"; - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mCrashRecoveryPropertiesMap; @@ -129,6 +134,17 @@ public class RescuePartyTest { @Captor private ArgumentCaptor<List<String>> mPackageListCaptor; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getFlags() { + return FlagsParameterization.allCombinationsOf( + Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS); + } + + public RescuePartyTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setUp() throws Exception { mSession = @@ -234,10 +250,10 @@ public class RescuePartyTest { } @Test - public void testBootLoopDetectionWithExecutionForAllRescueLevels() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testBootLoop() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -264,10 +280,22 @@ public class RescuePartyTest { noteBoot(5); assertTrue(RescueParty.isFactoryResetPropertySet()); } + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopNoFlags() { + // this is old test where the flag needs to be disabled + noteBoot(1); + assertTrue(RescueParty.isRebootPropertySet()); + + setCrashRecoveryPropAttemptingReboot(false); + noteBoot(2); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } @Test - public void testBootLoopDetectionWithExecutionForAllRescueLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopRecoverability() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -281,12 +309,14 @@ public class RescuePartyTest { final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; + noteBoot(1); noteBoot(2); assertTrue(RescueParty.isRebootPropertySet()); noteBoot(3); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); noteBoot(4); @@ -301,10 +331,10 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testPersistentAppCrash() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - noteAppCrash(1, true); noteAppCrash(2, true); noteAppCrash(3, true); @@ -318,8 +348,21 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevelsRecoverability() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testPersistentAppCrashNoFlags() { + // this is old test where the flag needs to be disabled + noteAppCrash(1, true); + assertTrue(RescueParty.isRebootPropertySet()); + + setCrashRecoveryPropAttemptingReboot(false); + noteAppCrash(2, true); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testPersistentAppCrashRecoverability() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -357,10 +400,10 @@ public class RescuePartyTest { } @Test - public void testNonPersistentAppDoesntDoAnything() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testNonPersistentApp() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - noteAppCrash(1, false); noteAppCrash(2, false); noteAppCrash(3, false); @@ -371,8 +414,9 @@ public class RescuePartyTest { } @Test + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testNonPersistentAppOnlyPerformsFlagResetsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -408,60 +452,6 @@ public class RescuePartyTest { } @Test - public void testNonPersistentAppCrashDetectionWithScopedResets() { - // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - - RescueParty.onSettingsProviderPublished(mMockContext); - verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), - any(Executor.class), - mMonitorCallbackCaptor.capture())); - - // Record DeviceConfig accesses - RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); - DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue(); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3); - - // Fake DeviceConfig value changes - monitorCallback.onNamespaceUpdate(NAMESPACE1); - verify(mMockPackageWatchdog).startObservingHealth(observer, - Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS); - monitorCallback.onNamespaceUpdate(NAMESPACE2); - verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer), - mPackageListCaptor.capture(), - eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS)); - monitorCallback.onNamespaceUpdate(NAMESPACE3); - verify(mMockPackageWatchdog).startObservingHealth(observer, - Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS); - assertTrue(mPackageListCaptor.getValue().containsAll( - Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2))); - // Perform and verify scoped resets - final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; - final String[] expectedAllResetNamespaces = - new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3}; - HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); - assertFalse(RescueParty.isRebootPropertySet()); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); - assertFalse(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testIsRecoveryTriggeredReboot() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); @@ -474,19 +464,6 @@ public class RescuePartyTest { } @Test - public void testIsRecoveryTriggeredRebootRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - assertFalse(RescueParty.isFactoryResetPropertySet()); - setCrashRecoveryPropAttemptingReboot(false); - noteBoot(RESCUE_LEVEL_FACTORY_RESET + 1); - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - assertTrue(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); @@ -505,25 +482,6 @@ public class RescuePartyTest { } @Test - public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompletedRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - int mitigationCount = RESCUE_LEVEL_FACTORY_RESET + 1; - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - setCrashRecoveryPropAttemptingReboot(false); - noteBoot(mitigationCount + 1); - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - assertTrue(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testThrottlingOnBootFailures() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -537,20 +495,6 @@ public class RescuePartyTest { } @Test - public void testThrottlingOnBootFailuresRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long beforeTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1); - setCrashRecoveryPropLastFactoryReset(beforeTimeout); - for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i); - } - assertFalse(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testThrottlingOnAppCrash() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -564,20 +508,6 @@ public class RescuePartyTest { } @Test - public void testThrottlingOnAppCrashRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long beforeTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1); - setCrashRecoveryPropLastFactoryReset(beforeTimeout); - for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteAppCrash(i + 1, true); - } - assertFalse(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testNotThrottlingAfterTimeoutOnBootFailures() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -591,20 +521,6 @@ public class RescuePartyTest { } @Test - public void testNotThrottlingAfterTimeoutOnBootFailuresRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long afterTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1); - setCrashRecoveryPropLastFactoryReset(afterTimeout); - for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i); - } - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -618,20 +534,7 @@ public class RescuePartyTest { } @Test - public void testNotThrottlingAfterTimeoutOnAppCrashRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long afterTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1); - setCrashRecoveryPropLastFactoryReset(afterTimeout); - for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteAppCrash(i + 1, true); - } - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testNativeRescuePartyResets() { doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed()); doReturn(FAKE_RESET_NATIVE_NAMESPACES).when( @@ -647,7 +550,6 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, @@ -660,7 +562,6 @@ public class RescuePartyTest { @Test public void testDisablingRescueByDeviceConfigFlag() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); @@ -686,24 +587,10 @@ public class RescuePartyTest { } @Test - public void testDisablingFactoryResetByDeviceConfigFlagRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); - - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - assertFalse(RescueParty.isFactoryResetPropertySet()); - - // Restore the property value initialized in SetUp() - SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, ""); - } - - @Test + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) public void testHealthCheckLevels() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); // Ensure that no action is taken for cases where the failure reason is unknown @@ -729,8 +616,9 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) public void testHealthCheckLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); // Ensure that no action is taken for cases where the failure reason is unknown @@ -767,11 +655,31 @@ public class RescuePartyTest { PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40); } + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testHealthCheckLevelsNoFlags() { + // this is old test where the flag needs to be disabled + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + // Ensure that no action is taken for cases where the failure reason is unknown + assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); + + // Ensure the correct user impact is returned for each mitigation count. + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); + } @Test + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) public void testBootLoopLevels() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -784,8 +692,9 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) public void testBootLoopLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40); @@ -797,6 +706,16 @@ public class RescuePartyTest { } @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopLevelsNoFlags() { + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); + } + + @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesOnlyRuntimeMap() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -827,6 +746,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesOnlyPresetMap() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -835,7 +755,7 @@ public class RescuePartyTest { String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," - + NAMESPACE3 + ":" + CALLING_PACKAGE1; + + NAMESPACE3 + ":" + CALLING_PACKAGE1; doReturn(presetMapping).when(() -> DeviceConfig.getString( eq(RescueParty.NAMESPACE_CONFIGURATION), eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), @@ -848,6 +768,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesBothMaps() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -884,6 +805,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigNoExceptionWhenFlagMalformed() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 0ddc38a78b1e..7ed26fb549f9 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -35,6 +35,7 @@ import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.flags.Flags; import java.util.HashMap; import java.util.HashSet; @@ -716,14 +717,15 @@ public final class TelephonyPermissions { } private static int getCarrierPrivilegeStatus(Context context, int subId, int uid) { - if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) { + if (isSystemOrPhone(uid)) { // Skip the check if it's one of these special uids return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } + final long identity = Binder.clearCallingIdentity(); try { TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService( - Context.TELEPHONY_SERVICE); + Context.TELEPHONY_SERVICE); return telephonyManager.createForSubscriptionId(subId).getCarrierPrivilegeStatus(uid); } finally { Binder.restoreCallingIdentity(identity); @@ -926,4 +928,30 @@ public final class TelephonyPermissions { || checkCallingOrSelfReadPhoneNumber(context, subId, callingPackage, callingFeatureId, message)); } + + /** + * @return true if the specified {@code uid} is for a system or phone process, no matter if runs + * as system user or not. + */ + public static boolean isSystemOrPhone(int uid) { + if (Flags.supportPhoneUidCheckForMultiuser()) { + return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid, + Process.PHONE_UID); + } else { + return uid == Process.SYSTEM_UID || uid == Process.PHONE_UID; + } + } + + /** + * @return true if the specified {@code uid} is for a ROOT or SHELL process, no matter if runs + * as system user or not. + */ + public static boolean isRootOrShell(int uid) { + if (Flags.supportPhoneUidCheckForMultiuser()) { + return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid, + Process.SHELL_UID); + } else { + return uid == Process.ROOT_UID || uid == Process.SHELL_UID; + } + } } |