Loading packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt +9 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import com.android.systemui.util.settings.SettingsUtilModule import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet import javax.inject.Named @Module(includes = [ FeatureFlagsDebugStartableModule::class, Loading @@ -35,7 +37,8 @@ abstract class FlagsModule { abstract fun bindsFeatureFlagDebug(impl: FeatureFlagsDebug): FeatureFlags @Binds abstract fun bindsRestarter(debugRestarter: FeatureFlagsDebugRestarter): Restarter @IntoSet abstract fun bindsScreenIdleCondition(impl: ScreenIdleCondition): ConditionalRestarter.Condition @Module companion object { Loading @@ -44,5 +47,10 @@ abstract class FlagsModule { fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager { return FlagManager(context, handler) } @JvmStatic @Provides @Named(ConditionalRestarter.RESTART_DELAY) fun provideRestartDelaySec(): Long = 1 } } packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt +17 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.systemui.flags import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet import javax.inject.Named @Module(includes = [ FeatureFlagsReleaseStartableModule::class, Loading @@ -29,5 +32,18 @@ abstract class FlagsModule { abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags @Binds abstract fun bindsRestarter(debugRestarter: FeatureFlagsReleaseRestarter): Restarter @IntoSet abstract fun bindsScreenIdleCondition(impl: ScreenIdleCondition): ConditionalRestarter.Condition @Binds @IntoSet abstract fun bindsPluggedInCondition(impl: PluggedInCondition): ConditionalRestarter.Condition @Module companion object { @JvmStatic @Provides @Named(ConditionalRestarter.RESTART_DELAY) fun provideRestartDelaySec(): Long = 30 } } packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt→packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * Copyright (C) 2023 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. Loading @@ -17,85 +17,87 @@ package com.android.systemui.flags import android.util.Log import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch /** Restarts SystemUI when the device appears idle. */ class FeatureFlagsReleaseRestarter /** Restarts the process after all passed in [Condition]s are true. */ class ConditionalRestarter @Inject constructor( private val wakefulnessLifecycle: WakefulnessLifecycle, private val batteryController: BatteryController, @Background private val bgExecutor: DelayableExecutor, private val systemExitRestarter: SystemExitRestarter private val systemExitRestarter: SystemExitRestarter, private val conditions: Set<@JvmSuppressWildcards Condition>, @Named(RESTART_DELAY) private val restartDelaySec: Long, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : Restarter { var listenersAdded = false var pendingRestart: Runnable? = null private var pendingReason = "" var androidRestartRequested = false val observer = object : WakefulnessLifecycle.Observer { override fun onFinishedGoingToSleep() { scheduleRestart(pendingReason) } } val batteryCallback = object : BatteryController.BatteryStateChangeCallback { override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) { scheduleRestart(pendingReason) } } private var restartJob: Job? = null private var pendingReason = "" private var androidRestartRequested = false override fun restartSystemUI(reason: String) { Log.d( FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting when plugged in and idle." ) Log.d(FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting when idle.") scheduleRestart(reason) } override fun restartAndroid(reason: String) { Log.d( FeatureFlagsDebug.TAG, "Android Restart requested. Restarting when plugged in and idle." ) Log.d(FeatureFlagsDebug.TAG, "Android Restart requested. Restarting when idle.") androidRestartRequested = true scheduleRestart(reason) } private fun scheduleRestart(reason: String) { // Don't bother adding listeners twice. pendingReason = reason if (!listenersAdded) { listenersAdded = true wakefulnessLifecycle.addObserver(observer) batteryController.addCallback(batteryCallback) private fun scheduleRestart(reason: String = "") { pendingReason = if (reason.isEmpty()) pendingReason else reason if (conditions.all { c -> c.canRestartNow(this::scheduleRestart) }) { if (restartJob == null) { restartJob = applicationScope.launch(backgroundDispatcher) { delay(TimeUnit.SECONDS.toMillis(restartDelaySec)) restartNow() } if ( wakefulnessLifecycle.wakefulness == WAKEFULNESS_ASLEEP && batteryController.isPluggedIn ) { if (pendingRestart == null) { pendingRestart = bgExecutor.executeDelayed(this::restartNow, 30L, TimeUnit.SECONDS) } } else if (pendingRestart != null) { pendingRestart?.run() pendingRestart = null } else { restartJob?.cancel() restartJob = null } } private fun restartNow() { Log.d(FeatureFlagsRelease.TAG, "Restarting due to systemui flag change") if (androidRestartRequested) { systemExitRestarter.restartAndroid(pendingReason) } else { systemExitRestarter.restartSystemUI(pendingReason) } } interface Condition { /** * Should return true if the system is ready to restart. * * A call to this function means that we want to restart and are waiting for this condition * to return true. * * retryFn should be cached if it is _not_ ready to restart, and later called when it _is_ * ready to restart. At that point, this method will be called again to verify that the * system is ready. * * Multiple calls to an instance of this method may happen for a single restart attempt if * multiple [Condition]s are being checked. If any one [Condition] returns false, all the * [Condition]s will need to be rechecked on the next restart attempt. */ fun canRestartNow(retryFn: () -> Unit): Boolean } companion object { const val RESTART_DELAY = "restarter_restart_delay" } } packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.systemui.flags import dagger.Binds import dagger.Module import dagger.Provides import javax.inject.Named Loading @@ -22,6 +23,8 @@ import javax.inject.Named /** Module containing shared code for all FeatureFlag implementations. */ @Module interface FlagsCommonModule { @Binds fun bindsRestarter(impl: ConditionalRestarter): Restarter companion object { const val ALL_FLAGS = "all_flags" Loading packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt 0 → 100644 +49 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.flags import com.android.systemui.statusbar.policy.BatteryController import javax.inject.Inject /** Returns true when the device is plugged in. */ class PluggedInCondition @Inject constructor( private val batteryController: BatteryController, ) : ConditionalRestarter.Condition { var listenersAdded = false var retryFn: (() -> Unit)? = null val batteryCallback = object : BatteryController.BatteryStateChangeCallback { override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) { retryFn?.invoke() } } override fun canRestartNow(retryFn: () -> Unit): Boolean { if (!listenersAdded) { listenersAdded = true batteryController.addCallback(batteryCallback) } this.retryFn = retryFn return batteryController.isPluggedIn } } Loading
packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt +9 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import com.android.systemui.util.settings.SettingsUtilModule import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet import javax.inject.Named @Module(includes = [ FeatureFlagsDebugStartableModule::class, Loading @@ -35,7 +37,8 @@ abstract class FlagsModule { abstract fun bindsFeatureFlagDebug(impl: FeatureFlagsDebug): FeatureFlags @Binds abstract fun bindsRestarter(debugRestarter: FeatureFlagsDebugRestarter): Restarter @IntoSet abstract fun bindsScreenIdleCondition(impl: ScreenIdleCondition): ConditionalRestarter.Condition @Module companion object { Loading @@ -44,5 +47,10 @@ abstract class FlagsModule { fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager { return FlagManager(context, handler) } @JvmStatic @Provides @Named(ConditionalRestarter.RESTART_DELAY) fun provideRestartDelaySec(): Long = 1 } }
packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt +17 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.systemui.flags import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet import javax.inject.Named @Module(includes = [ FeatureFlagsReleaseStartableModule::class, Loading @@ -29,5 +32,18 @@ abstract class FlagsModule { abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags @Binds abstract fun bindsRestarter(debugRestarter: FeatureFlagsReleaseRestarter): Restarter @IntoSet abstract fun bindsScreenIdleCondition(impl: ScreenIdleCondition): ConditionalRestarter.Condition @Binds @IntoSet abstract fun bindsPluggedInCondition(impl: PluggedInCondition): ConditionalRestarter.Condition @Module companion object { @JvmStatic @Provides @Named(ConditionalRestarter.RESTART_DELAY) fun provideRestartDelaySec(): Long = 30 } }
packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt→packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * Copyright (C) 2023 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. Loading @@ -17,85 +17,87 @@ package com.android.systemui.flags import android.util.Log import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch /** Restarts SystemUI when the device appears idle. */ class FeatureFlagsReleaseRestarter /** Restarts the process after all passed in [Condition]s are true. */ class ConditionalRestarter @Inject constructor( private val wakefulnessLifecycle: WakefulnessLifecycle, private val batteryController: BatteryController, @Background private val bgExecutor: DelayableExecutor, private val systemExitRestarter: SystemExitRestarter private val systemExitRestarter: SystemExitRestarter, private val conditions: Set<@JvmSuppressWildcards Condition>, @Named(RESTART_DELAY) private val restartDelaySec: Long, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : Restarter { var listenersAdded = false var pendingRestart: Runnable? = null private var pendingReason = "" var androidRestartRequested = false val observer = object : WakefulnessLifecycle.Observer { override fun onFinishedGoingToSleep() { scheduleRestart(pendingReason) } } val batteryCallback = object : BatteryController.BatteryStateChangeCallback { override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) { scheduleRestart(pendingReason) } } private var restartJob: Job? = null private var pendingReason = "" private var androidRestartRequested = false override fun restartSystemUI(reason: String) { Log.d( FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting when plugged in and idle." ) Log.d(FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting when idle.") scheduleRestart(reason) } override fun restartAndroid(reason: String) { Log.d( FeatureFlagsDebug.TAG, "Android Restart requested. Restarting when plugged in and idle." ) Log.d(FeatureFlagsDebug.TAG, "Android Restart requested. Restarting when idle.") androidRestartRequested = true scheduleRestart(reason) } private fun scheduleRestart(reason: String) { // Don't bother adding listeners twice. pendingReason = reason if (!listenersAdded) { listenersAdded = true wakefulnessLifecycle.addObserver(observer) batteryController.addCallback(batteryCallback) private fun scheduleRestart(reason: String = "") { pendingReason = if (reason.isEmpty()) pendingReason else reason if (conditions.all { c -> c.canRestartNow(this::scheduleRestart) }) { if (restartJob == null) { restartJob = applicationScope.launch(backgroundDispatcher) { delay(TimeUnit.SECONDS.toMillis(restartDelaySec)) restartNow() } if ( wakefulnessLifecycle.wakefulness == WAKEFULNESS_ASLEEP && batteryController.isPluggedIn ) { if (pendingRestart == null) { pendingRestart = bgExecutor.executeDelayed(this::restartNow, 30L, TimeUnit.SECONDS) } } else if (pendingRestart != null) { pendingRestart?.run() pendingRestart = null } else { restartJob?.cancel() restartJob = null } } private fun restartNow() { Log.d(FeatureFlagsRelease.TAG, "Restarting due to systemui flag change") if (androidRestartRequested) { systemExitRestarter.restartAndroid(pendingReason) } else { systemExitRestarter.restartSystemUI(pendingReason) } } interface Condition { /** * Should return true if the system is ready to restart. * * A call to this function means that we want to restart and are waiting for this condition * to return true. * * retryFn should be cached if it is _not_ ready to restart, and later called when it _is_ * ready to restart. At that point, this method will be called again to verify that the * system is ready. * * Multiple calls to an instance of this method may happen for a single restart attempt if * multiple [Condition]s are being checked. If any one [Condition] returns false, all the * [Condition]s will need to be rechecked on the next restart attempt. */ fun canRestartNow(retryFn: () -> Unit): Boolean } companion object { const val RESTART_DELAY = "restarter_restart_delay" } }
packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.systemui.flags import dagger.Binds import dagger.Module import dagger.Provides import javax.inject.Named Loading @@ -22,6 +23,8 @@ import javax.inject.Named /** Module containing shared code for all FeatureFlag implementations. */ @Module interface FlagsCommonModule { @Binds fun bindsRestarter(impl: ConditionalRestarter): Restarter companion object { const val ALL_FLAGS = "all_flags" Loading
packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt 0 → 100644 +49 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.flags import com.android.systemui.statusbar.policy.BatteryController import javax.inject.Inject /** Returns true when the device is plugged in. */ class PluggedInCondition @Inject constructor( private val batteryController: BatteryController, ) : ConditionalRestarter.Condition { var listenersAdded = false var retryFn: (() -> Unit)? = null val batteryCallback = object : BatteryController.BatteryStateChangeCallback { override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) { retryFn?.invoke() } } override fun canRestartNow(retryFn: () -> Unit): Boolean { if (!listenersAdded) { listenersAdded = true batteryController.addCallback(batteryCallback) } this.retryFn = retryFn return batteryController.isPluggedIn } }