[Central Surfaces] Move #wakeUpIfDozing to PowerRepository.

Also moves PulsingGestureListener's calls to #wakeUpIfDozing to the
power interactor.

Other references to CentralSurfaces#wakeUpIfDozing will be moved in
future CLs.

Bug: 277762009
Bug: 277764509
Test: Single tap on AOD -> verify device wakes with reason
"PULSING_SINGLE_TAP"
Test: Double tap on AOD -> verify device wakes with reason
"PULSING_DOUBLE_TAP"
Test: atest PowerRepositoryImplTest PowerInteractorTest
Test: atest PulsingGestureListenerTest

Change-Id: I96d2ec9343fc9a329dc3142800db41d2ddfe4510
This commit is contained in:
Caitlin Shkuratov
2023-05-31 16:54:18 +00:00
parent b9777bc49b
commit 32eeaa1493
9 changed files with 180 additions and 36 deletions

View File

@@ -26,6 +26,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -34,13 +36,18 @@ import kotlinx.coroutines.flow.Flow
interface PowerRepository {
/** Whether the device is interactive. Starts with the current state. */
val isInteractive: Flow<Boolean>
/** Wakes up the device. */
fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
}
@SysUISingleton
class PowerRepositoryImpl
@Inject
constructor(
manager: PowerManager,
private val manager: PowerManager,
@Application private val applicationContext: Context,
private val systemClock: SystemClock,
dispatcher: BroadcastDispatcher,
) : PowerRepository {
@@ -68,6 +75,14 @@ constructor(
awaitClose { dispatcher.unregisterReceiver(receiver) }
}
override fun wakeUp(why: String, wakeReason: Int) {
manager.wakeUp(
systemClock.uptimeMillis(),
wakeReason,
"${applicationContext.packageName}:$why",
)
}
companion object {
private const val TAG = "PowerRepository"
}

View File

@@ -17,8 +17,12 @@
package com.android.systemui.power.domain.interactor
import android.os.PowerManager
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -27,8 +31,27 @@ import kotlinx.coroutines.flow.Flow
class PowerInteractor
@Inject
constructor(
repository: PowerRepository,
private val repository: PowerRepository,
private val falsingCollector: FalsingCollector,
private val screenOffAnimationController: ScreenOffAnimationController,
private val statusBarStateController: StatusBarStateController,
) {
/** Whether the screen is on or off. */
val isInteractive: Flow<Boolean> = repository.isInteractive
/**
* Wakes up the device if the device was dozing.
*
* @param why a string explaining why we're waking the device for debugging purposes. Should be
* in SCREAMING_SNAKE_CASE.
* @param wakeReason the PowerManager-based reason why we're waking the device.
*/
fun wakeUpIfDozing(why: String, @PowerManager.WakeReason wakeReason: Int) {
if (
statusBarStateController.isDozing && screenOffAnimationController.allowWakeUpIfDozing()
) {
repository.wakeUp(why, wakeReason)
falsingCollector.onScreenOnFromTouch()
}
}
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.shade
import android.hardware.display.AmbientDisplayConfiguration
import android.os.PowerManager
import android.os.SystemClock
import android.provider.Settings
import android.view.GestureDetector
import android.view.MotionEvent
@@ -28,8 +27,8 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
@@ -50,7 +49,7 @@ class PulsingGestureListener @Inject constructor(
private val notificationShadeWindowView: NotificationShadeWindowView,
private val falsingManager: FalsingManager,
private val dockManager: DockManager,
private val centralSurfaces: CentralSurfaces,
private val powerInteractor: PowerInteractor,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val statusBarStateController: StatusBarStateController,
private val shadeLogger: ShadeLogger,
@@ -88,11 +87,7 @@ class PulsingGestureListener @Inject constructor(
shadeLogger.logSingleTapUpFalsingState(proximityIsNotNear, isNotAFalseTap)
if (proximityIsNotNear && isNotAFalseTap) {
shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing")
centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
"PULSING_SINGLE_TAP",
PowerManager.WAKE_REASON_TAP
)
powerInteractor.wakeUpIfDozing("PULSING_SINGLE_TAP", PowerManager.WAKE_REASON_TAP)
}
return true
}
@@ -113,11 +108,7 @@ class PulsingGestureListener @Inject constructor(
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
) {
centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
"PULSING_DOUBLE_TAP",
PowerManager.WAKE_REASON_TAP
)
powerInteractor.wakeUpIfDozing("PULSING_DOUBLE_TAP", PowerManager.WAKE_REASON_TAP)
return true
}
return false

View File

@@ -43,6 +43,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -197,7 +198,10 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
/**
* Wakes up the device if the device was dozing.
*
* @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead.
*/
@Deprecated
void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason);
/** */

View File

@@ -136,6 +136,7 @@ import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenu
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.charging.WiredChargingRippleController;
@@ -157,7 +158,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
@@ -1587,10 +1587,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
/**
* Ask the display to wake up if currently dozing, else do nothing
*
* @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead.
*
* @param time when to wake up
* @param why the reason for the wake up
*/
@Override
@Deprecated
public void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason) {
if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) {
mPowerManager.wakeUp(

View File

@@ -24,7 +24,11 @@ import android.os.PowerManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
@@ -47,6 +51,8 @@ import org.mockito.MockitoAnnotations
@RunWith(JUnit4::class)
class PowerRepositoryImplTest : SysuiTestCase() {
private val systemClock = FakeSystemClock()
@Mock private lateinit var manager: PowerManager
@Mock private lateinit var dispatcher: BroadcastDispatcher
@Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
@@ -62,7 +68,13 @@ class PowerRepositoryImplTest : SysuiTestCase() {
isInteractive = true
whenever(manager.isInteractive).then { isInteractive }
underTest = PowerRepositoryImpl(manager = manager, dispatcher = dispatcher)
underTest =
PowerRepositoryImpl(
manager,
context.applicationContext,
systemClock,
dispatcher,
)
}
@Test
@@ -160,6 +172,27 @@ class PowerRepositoryImplTest : SysuiTestCase() {
job.cancel()
}
@Test
fun wakeUp_notifiesPowerManager() {
systemClock.setUptimeMillis(345000)
underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE)
val reasonCaptor = argumentCaptor<String>()
verify(manager)
.wakeUp(eq(345000L), eq(PowerManager.WAKE_REASON_GESTURE), capture(reasonCaptor))
assertThat(reasonCaptor.value).contains("fakeWhy")
}
@Test
fun wakeUp_usesApplicationPackageName() {
underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE)
val reasonCaptor = argumentCaptor<String>()
verify(manager).wakeUp(any(), any(), capture(reasonCaptor))
assertThat(reasonCaptor.value).contains(context.applicationContext.packageName)
}
private fun verifyRegistered() {
// We must verify with all arguments, even those that are optional because they have default
// values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be

View File

@@ -17,9 +17,14 @@
package com.android.systemui.power.domain.interactor
import android.os.PowerManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
@@ -29,6 +34,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
@@ -36,14 +44,25 @@ class PowerInteractorTest : SysuiTestCase() {
private lateinit var underTest: PowerInteractor
private lateinit var repository: FakePowerRepository
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
repository =
FakePowerRepository(
initialInteractive = true,
)
underTest = PowerInteractor(repository = repository)
underTest =
PowerInteractor(
repository,
falsingCollector,
screenOffAnimationController,
statusBarStateController,
)
}
@Test
@@ -72,6 +91,40 @@ class PowerInteractorTest : SysuiTestCase() {
job.cancel()
}
@Test
fun wakeUpIfDozing_notDozing_notWoken() {
whenever(statusBarStateController.isDozing).thenReturn(false)
whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
underTest.wakeUpIfDozing("why", PowerManager.WAKE_REASON_TAP)
assertThat(repository.lastWakeWhy).isNull()
assertThat(repository.lastWakeReason).isNull()
}
@Test
fun wakeUpIfDozing_notAllowed_notWoken() {
whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(false)
whenever(statusBarStateController.isDozing).thenReturn(true)
underTest.wakeUpIfDozing("why", PowerManager.WAKE_REASON_TAP)
assertThat(repository.lastWakeWhy).isNull()
assertThat(repository.lastWakeReason).isNull()
}
@Test
fun wakeUpIfDozing_dozingAndAllowed_wokenAndFalsingNotified() {
whenever(statusBarStateController.isDozing).thenReturn(true)
whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
underTest.wakeUpIfDozing("testReason", PowerManager.WAKE_REASON_GESTURE)
assertThat(repository.lastWakeWhy).isEqualTo("testReason")
assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE)
verify(falsingCollector).onScreenOnFromTouch()
}
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
}

View File

@@ -25,22 +25,24 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
import com.android.systemui.util.mockito.eq
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -54,12 +56,12 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Mock
private lateinit var view: NotificationShadeWindowView
@Mock
private lateinit var centralSurfaces: CentralSurfaces
@Mock
private lateinit var dockManager: DockManager
@Mock
private lateinit var falsingManager: FalsingManager
@Mock
private lateinit var falsingCollector: FalsingCollector
@Mock
private lateinit var ambientDisplayConfiguration: AmbientDisplayConfiguration
@Mock
private lateinit var tunerService: TunerService
@@ -71,7 +73,10 @@ class PulsingGestureListenerTest : SysuiTestCase() {
private lateinit var shadeLogger: ShadeLogger
@Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var screenOffAnimationController: ScreenOffAnimationController
private lateinit var powerRepository: FakePowerRepository
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -79,11 +84,18 @@ class PulsingGestureListenerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
powerRepository = FakePowerRepository()
underTest = PulsingGestureListener(
view,
falsingManager,
dockManager,
centralSurfaces,
PowerInteractor(
powerRepository,
falsingCollector,
screenOffAnimationController,
statusBarStateController,
),
ambientDisplayConfiguration,
statusBarStateController,
shadeLogger,
@@ -92,6 +104,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
dumpManager
)
whenever(dockManager.isDocked).thenReturn(false)
whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
}
@Test
@@ -110,8 +123,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onSingleTapUp(upEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(
anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
assertThat(powerRepository.lastWakeWhy).isNotNull()
assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP)
}
@Test
@@ -130,8 +143,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onDoubleTapEvent(upEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(
anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
assertThat(powerRepository.lastWakeWhy).isNotNull()
assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP)
}
@Test
@@ -162,8 +175,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onSingleTapUp(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(
anyLong(), anyString(), anyInt())
assertThat(powerRepository.lastWakeWhy).isNull()
assertThat(powerRepository.lastWakeReason).isNull()
}
@Test
@@ -210,8 +223,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onDoubleTapEvent(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(
anyLong(), anyString(), anyInt())
assertThat(powerRepository.lastWakeWhy).isNull()
assertThat(powerRepository.lastWakeReason).isNull()
}
@Test
@@ -230,8 +243,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onSingleTapUp(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(
anyLong(), anyString(), anyInt())
assertThat(powerRepository.lastWakeWhy).isNull()
assertThat(powerRepository.lastWakeReason).isNull()
}
@Test
@@ -250,8 +263,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
underTest.onDoubleTapEvent(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(
anyLong(), anyString(), anyInt())
assertThat(powerRepository.lastWakeWhy).isNull()
assertThat(powerRepository.lastWakeReason).isNull()
}
fun updateSettings() {

View File

@@ -17,6 +17,7 @@
package com.android.systemui.power.data.repository
import android.os.PowerManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -28,7 +29,15 @@ class FakePowerRepository(
private val _isInteractive = MutableStateFlow(initialInteractive)
override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow()
var lastWakeWhy: String? = null
var lastWakeReason: Int? = null
fun setInteractive(value: Boolean) {
_isInteractive.value = value
}
override fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int) {
lastWakeWhy = why
lastWakeReason = wakeReason
}
}