Check that PowerManager#isInteractive is false before showing the AOD UI for screen off.
The callback that runs after a delay during the screen off animation to
show the AOD UI after the light reveal animation is supposed to be
cancelled in onStartedWakingUp if the screen off is cancelled. However,
some bugs make it seem like this may not be happening, resulting in the
AOD UI being shown while we are awake and unlocked. This causes the shade
to look like the lock screen. It also causes a transparent shade
background and possibly an all-white screen.
While we have not been able to reproduce this under normal conditions, I
manually called showAodUi while unlocked/awake, and verified that these
conditions result. Since onStartedWakingUp is dispatched asynchronously,
it's possible that the device wakes up before the callback is cancelled.
By checking PowerManager's state directly, we can avoid this.
Fixes: 213794749
Test: atest SystemUITests
Merged-In: I7e2d1ca7e3c9487903f74fb06575cc0a36b9502c
Change-Id: I7e2d1ca7e3c9487903f74fb06575cc0a36b9502c
(cherry picked from commit 7ecf6bb70a)
Merged-In:I7e2d1ca7e3c9487903f74fb06575cc0a36b9502c
This commit is contained in:
committed by
Android Build Coastguard Worker
parent
4e0b08b5c1
commit
955d37e6d7
@@ -6,6 +6,7 @@ import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
@@ -50,9 +51,10 @@ class UnlockedScreenOffAnimationController @Inject constructor(
|
||||
private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val dozeParameters: dagger.Lazy<DozeParameters>,
|
||||
private val globalSettings: GlobalSettings
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val powerManager: PowerManager,
|
||||
private val handler: Handler = Handler()
|
||||
) : WakefulnessLifecycle.Observer {
|
||||
private val handler = Handler()
|
||||
|
||||
private lateinit var statusBar: StatusBar
|
||||
private lateinit var lightRevealScrim: LightRevealScrim
|
||||
@@ -205,10 +207,18 @@ class UnlockedScreenOffAnimationController @Inject constructor(
|
||||
lightRevealAnimationPlaying = true
|
||||
lightRevealAnimator.start()
|
||||
handler.postDelayed({
|
||||
aodUiAnimationPlaying = true
|
||||
// Only run this callback if the device is sleeping (not interactive). This callback
|
||||
// is removed in onStartedWakingUp, but since that event is asynchronously
|
||||
// dispatched, a race condition could make it possible for this callback to be run
|
||||
// as the device is waking up. That results in the AOD UI being shown while we wake
|
||||
// up, with unpredictable consequences.
|
||||
if (!powerManager.isInteractive) {
|
||||
aodUiAnimationPlaying = true
|
||||
|
||||
// Show AOD. That'll cause the KeyguardVisibilityHelper to call #animateInKeyguard.
|
||||
statusBar.notificationPanelViewController.showAodUi()
|
||||
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
|
||||
// #animateInKeyguard.
|
||||
statusBar.notificationPanelViewController.showAodUi()
|
||||
}
|
||||
}, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
|
||||
} else {
|
||||
decidedToAnimateGoingToSleep = false
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.android.systemui.statusbar.phone
|
||||
|
||||
import android.animation.Animator
|
||||
import android.os.Handler
|
||||
import android.os.PowerManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.View
|
||||
@@ -28,13 +30,20 @@ import com.android.systemui.statusbar.LightRevealScrim
|
||||
import com.android.systemui.statusbar.StatusBarStateControllerImpl
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.anyLong
|
||||
import org.mockito.Mockito.mockingDetails
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@@ -52,13 +61,19 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var globalSettings: GlobalSettings
|
||||
@Mock
|
||||
private lateinit var statusbar: StatusBar
|
||||
private lateinit var statusBar: StatusBar
|
||||
@Mock
|
||||
private lateinit var notificationPanelViewController: NotificationPanelViewController
|
||||
@Mock
|
||||
private lateinit var lightRevealScrim: LightRevealScrim
|
||||
@Mock
|
||||
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
|
||||
@Mock
|
||||
private lateinit var statusBarStateController: StatusBarStateControllerImpl
|
||||
@Mock
|
||||
private lateinit var powerManager: PowerManager
|
||||
@Mock
|
||||
private lateinit var handler: Handler
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -71,9 +86,24 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
|
||||
dagger.Lazy<KeyguardViewMediator> { keyguardViewMediator },
|
||||
keyguardStateController,
|
||||
dagger.Lazy<DozeParameters> { dozeParameters },
|
||||
globalSettings
|
||||
globalSettings,
|
||||
powerManager,
|
||||
handler = handler
|
||||
)
|
||||
controller.initialize(statusbar, lightRevealScrim)
|
||||
controller.initialize(statusBar, lightRevealScrim)
|
||||
`when`(statusBar.notificationPanelViewController).thenReturn(
|
||||
notificationPanelViewController)
|
||||
|
||||
// Screen off does not run if the panel is expanded, so we should say it's collapsed to test
|
||||
// screen off.
|
||||
`when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
// Tell the screen off controller to cancel the animations and clean up its state, or
|
||||
// subsequent tests will act unpredictably as the animator continues running.
|
||||
controller.onStartedWakingUp()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,4 +119,51 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
|
||||
listener.value.onAnimationEnd(null)
|
||||
Mockito.verify(animator).setListener(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
|
||||
* animation to start. If the device is woken up during the screen off, we should *never* do
|
||||
* this.
|
||||
*
|
||||
* This test confirms that we do show the AOD UI when the device is not woken up
|
||||
* (PowerManager#isInteractive = false).
|
||||
*/
|
||||
@Test
|
||||
fun testAodUiShownIfNotInteractive() {
|
||||
`when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
|
||||
`when`(powerManager.isInteractive).thenReturn(false)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
|
||||
controller.onStartedGoingToSleep()
|
||||
|
||||
verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
|
||||
|
||||
callbackCaptor.value.run()
|
||||
|
||||
verify(notificationPanelViewController, times(1)).showAodUi()
|
||||
}
|
||||
|
||||
/**
|
||||
* The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
|
||||
* animation to start. If the device is woken up during the screen off, we should *never* do
|
||||
* this.
|
||||
*
|
||||
* This test confirms that we do not show the AOD UI when the device is woken up during screen
|
||||
* off (PowerManager#isInteractive = true).
|
||||
*/
|
||||
@Test
|
||||
fun testAodUiNotShownIfInteractive() {
|
||||
`when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
|
||||
`when`(powerManager.isInteractive).thenReturn(true)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
|
||||
controller.onStartedGoingToSleep()
|
||||
|
||||
mockingDetails(handler).printInvocations()
|
||||
|
||||
verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
|
||||
callbackCaptor.value.run()
|
||||
|
||||
verify(notificationPanelViewController, never()).showAodUi()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user