Merge "Restart after flag changes on screen off." into tm-qpr-dev

This commit is contained in:
Dave Mankoff
2022-11-15 21:39:54 +00:00
committed by Android (Google) Code Review
9 changed files with 378 additions and 12 deletions

View File

@@ -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

View File

@@ -27,4 +27,7 @@ import dagger.Module
abstract class FlagsModule {
@Binds
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
@Binds
abstract fun bindsRestarter(debugRestarter: FeatureFlagsReleaseRestarter): Restarter
}

View File

@@ -336,7 +336,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
Log.i(TAG, "Android Restart Suppressed");
return;
}
Log.i(TAG, "Restarting Android");
mRestarter.restart();
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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)
}
}