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

Commit abc951df authored by Dave Mankoff's avatar Dave Mankoff Committed by Android (Google) Code Review
Browse files

Merge "Delay restarting SystemUI on debug builds." into tm-qpr-dev

parents 9b204ac5 72546267
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -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,
@@ -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 {
@@ -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
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -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,
@@ -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
    }
}
+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.
@@ -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"
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.systemui.flags

import dagger.Binds
import dagger.Module
import dagger.Provides
import javax.inject.Named
@@ -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"

+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