Restart after flag changes on screen off.
On debug builds, restart as soon as the screen goes off. On release builds, restart after the device is plugged in, the screen is off, and the device has been idle for a few seconds. The flag app does not yet reflect this change, but it will allow multiple flags to be flipped. Bug: 257302229 Test: manual Change-Id: I9da58f9881973d69fc0e35b394ca42a4166ceb08
This commit is contained in:
@@ -34,6 +34,9 @@ abstract class FlagsModule {
|
||||
@Binds
|
||||
abstract fun bindsFeatureFlagDebug(impl: FeatureFlagsDebug): FeatureFlags
|
||||
|
||||
@Binds
|
||||
abstract fun bindsRestarter(debugRestarter: FeatureFlagsDebugRestarter): Restarter
|
||||
|
||||
@Module
|
||||
companion object {
|
||||
@JvmStatic
|
||||
|
||||
@@ -27,4 +27,7 @@ import dagger.Module
|
||||
abstract class FlagsModule {
|
||||
@Binds
|
||||
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
|
||||
|
||||
@Binds
|
||||
abstract fun bindsRestarter(debugRestarter: FeatureFlagsReleaseRestarter): Restarter
|
||||
}
|
||||
|
||||
@@ -336,7 +336,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
|
||||
Log.i(TAG, "Android Restart Suppressed");
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Restarting Android");
|
||||
mRestarter.restart();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.util.Log
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Restarts SystemUI when the screen is locked. */
|
||||
class FeatureFlagsDebugRestarter
|
||||
@Inject
|
||||
constructor(
|
||||
private val wakefulnessLifecycle: WakefulnessLifecycle,
|
||||
private val systemExitRestarter: SystemExitRestarter,
|
||||
) : Restarter {
|
||||
|
||||
val observer =
|
||||
object : WakefulnessLifecycle.Observer {
|
||||
override fun onFinishedGoingToSleep() {
|
||||
Log.d(FeatureFlagsDebug.TAG, "Restarting due to systemui flag change")
|
||||
restartNow()
|
||||
}
|
||||
}
|
||||
|
||||
override fun restart() {
|
||||
Log.d(FeatureFlagsDebug.TAG, "Restart requested. Restarting on next screen off.")
|
||||
if (wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP) {
|
||||
restartNow()
|
||||
} else {
|
||||
wakefulnessLifecycle.addObserver(observer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restartNow() {
|
||||
systemExitRestarter.restart()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.util.Log
|
||||
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
|
||||
|
||||
/** Restarts SystemUI when the device appears idle. */
|
||||
class FeatureFlagsReleaseRestarter
|
||||
@Inject
|
||||
constructor(
|
||||
private val wakefulnessLifecycle: WakefulnessLifecycle,
|
||||
private val batteryController: BatteryController,
|
||||
@Background private val bgExecutor: DelayableExecutor,
|
||||
private val systemExitRestarter: SystemExitRestarter
|
||||
) : Restarter {
|
||||
var shouldRestart = false
|
||||
var pendingRestart: Runnable? = null
|
||||
|
||||
val observer =
|
||||
object : WakefulnessLifecycle.Observer {
|
||||
override fun onFinishedGoingToSleep() {
|
||||
maybeScheduleRestart()
|
||||
}
|
||||
}
|
||||
|
||||
val batteryCallback =
|
||||
object : BatteryController.BatteryStateChangeCallback {
|
||||
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
|
||||
maybeScheduleRestart()
|
||||
}
|
||||
}
|
||||
|
||||
override fun restart() {
|
||||
Log.d(FeatureFlagsDebug.TAG, "Restart requested. Restarting when plugged in and idle.")
|
||||
if (!shouldRestart) {
|
||||
// Don't bother scheduling twice.
|
||||
shouldRestart = true
|
||||
wakefulnessLifecycle.addObserver(observer)
|
||||
batteryController.addCallback(batteryCallback)
|
||||
maybeScheduleRestart()
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeScheduleRestart() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private fun restartNow() {
|
||||
Log.d(FeatureFlagsRelease.TAG, "Restarting due to systemui flag change")
|
||||
systemExitRestarter.restart()
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.android.systemui.flags
|
||||
|
||||
import com.android.internal.statusbar.IStatusBarService
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Named
|
||||
@@ -32,15 +31,5 @@ interface FlagsCommonModule {
|
||||
fun providesAllFlags(): Map<Int, Flag<*>> {
|
||||
return FlagsFactory.knownFlags.map { it.value.id to it.value }.toMap()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
fun providesRestarter(barService: IStatusBarService): Restarter {
|
||||
return object : Restarter {
|
||||
override fun restart() {
|
||||
barService.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 javax.inject.Inject
|
||||
|
||||
class SystemExitRestarter @Inject constructor() : Restarter {
|
||||
override fun restart() {
|
||||
System.exit(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.test.suitebuilder.annotation.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/**
|
||||
* Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
|
||||
*/
|
||||
@SmallTest
|
||||
class FeatureFlagsDebugRestarterTest : SysuiTestCase() {
|
||||
private lateinit var restarter: FeatureFlagsDebugRestarter
|
||||
|
||||
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
|
||||
@Mock private lateinit var systemExitRestarter: SystemExitRestarter
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
restarter = FeatureFlagsDebugRestarter(wakefulnessLifecycle, systemExitRestarter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_ImmediateWhenAsleep() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
restarter.restart()
|
||||
verify(systemExitRestarter).restart()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_WaitsForSceenOff() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
|
||||
|
||||
restarter.restart()
|
||||
verify(systemExitRestarter, never()).restart()
|
||||
|
||||
val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
|
||||
verify(wakefulnessLifecycle).addObserver(captor.capture())
|
||||
|
||||
captor.value.onFinishedGoingToSleep()
|
||||
|
||||
verify(systemExitRestarter).restart()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.test.suitebuilder.annotation.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP
|
||||
import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
|
||||
import com.android.systemui.statusbar.policy.BatteryController
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/**
|
||||
* Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
|
||||
*/
|
||||
@SmallTest
|
||||
class FeatureFlagsReleaseRestarterTest : SysuiTestCase() {
|
||||
private lateinit var restarter: FeatureFlagsReleaseRestarter
|
||||
|
||||
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
|
||||
@Mock private lateinit var batteryController: BatteryController
|
||||
@Mock private lateinit var systemExitRestarter: SystemExitRestarter
|
||||
private val executor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
restarter =
|
||||
FeatureFlagsReleaseRestarter(
|
||||
wakefulnessLifecycle,
|
||||
batteryController,
|
||||
executor,
|
||||
systemExitRestarter
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_ScheduledWhenReady() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
assertThat(executor.numPending()).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_RestartsWhenIdle() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
|
||||
restarter.restart()
|
||||
verify(systemExitRestarter, never()).restart()
|
||||
executor.advanceClockToLast()
|
||||
executor.runAllReady()
|
||||
verify(systemExitRestarter).restart()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_NotScheduledWhenAwake() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_NotScheduledWhenNotPluggedIn() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(false)
|
||||
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRestart_NotDoubleSheduled() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
restarter.restart()
|
||||
assertThat(executor.numPending()).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWakefulnessLifecycle_CanRestart() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
|
||||
val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
|
||||
verify(wakefulnessLifecycle).addObserver(captor.capture())
|
||||
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
|
||||
captor.value.onFinishedGoingToSleep()
|
||||
assertThat(executor.numPending()).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBatteryController_CanRestart() {
|
||||
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
|
||||
whenever(batteryController.isPluggedIn).thenReturn(false)
|
||||
assertThat(executor.numPending()).isEqualTo(0)
|
||||
restarter.restart()
|
||||
|
||||
val captor =
|
||||
ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback::class.java)
|
||||
verify(batteryController).addCallback(captor.capture())
|
||||
|
||||
whenever(batteryController.isPluggedIn).thenReturn(true)
|
||||
|
||||
captor.value.onBatteryLevelChanged(0, true, true)
|
||||
assertThat(executor.numPending()).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user