Loading core/java/android/service/dreams/DreamService.java +10 −1 Original line number Diff line number Diff line Loading @@ -1276,13 +1276,22 @@ public class DreamService extends Service implements Window.Callback { }); } /** * Whether or not wake requests will be redirected. * * @hide */ public boolean getRedirectWake() { return mOverlayConnection != null && mRedirectWake; } private void wakeUp(boolean fromSystem) { if (mDebug) { Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking + ", mFinished=" + mFinished); } if (!fromSystem && mOverlayConnection != null && mRedirectWake) { if (!fromSystem && getRedirectWake()) { mOverlayConnection.addConsumer(overlay -> { try { overlay.onWakeRequested(); Loading packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt +103 −35 Original line number Diff line number Diff line Loading @@ -16,29 +16,40 @@ package com.android.systemui.dreams.homecontrols import android.app.Activity import android.content.Intent import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_ACTIVITY_PANEL import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_DREAM import android.service.controls.ControlsProviderService.EXTRA_CONTROLS_SURFACE import android.window.TaskFragmentInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.core.FakeLogBuffer.Factory.Companion.create import com.android.systemui.log.logcatLogBuffer import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.android.systemui.util.wakelock.WakeLockFake import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.argThat import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class HomeControlsDreamServiceTest : SysuiTestCase() { Loading @@ -46,30 +57,37 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder private lateinit var fakeWakeLock: WakeLockFake private val fakeWakeLock = WakeLockFake() private val fakeWakeLockBuilder by lazy { WakeLockFake.Builder(context).apply { setWakeLock(fakeWakeLock) } } @Mock private lateinit var taskFragmentComponentFactory: TaskFragmentComponent.Factory @Mock private lateinit var taskFragmentComponent: TaskFragmentComponent @Mock private lateinit var activity: Activity private val taskFragmentComponent = mock<TaskFragmentComponent>() private val activity = mock<Activity>() private val onCreateCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>() private val onInfoChangedCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>() private val hideCallback = argumentCaptor<() -> Unit>() private val dreamServiceDelegate = mock<DreamServiceDelegate> { on { getActivity(any()) } doReturn activity } private val taskFragmentComponentFactory = mock<TaskFragmentComponent.Factory> { on { create( activity = eq(activity), onCreateCallback = onCreateCallback.capture(), onInfoChangedCallback = onInfoChangedCallback.capture(), hide = hideCallback.capture(), ) } doReturn taskFragmentComponent } private lateinit var underTest: HomeControlsDreamService private val underTest: HomeControlsDreamService by lazy { buildService() } @Before fun setup() = with(kosmos) { MockitoAnnotations.initMocks(this@HomeControlsDreamServiceTest) whenever(taskFragmentComponentFactory.create(any(), any(), any(), any())) .thenReturn(taskFragmentComponent) fakeWakeLock = WakeLockFake() fakeWakeLockBuilder = WakeLockFake.Builder(context) fakeWakeLockBuilder.setWakeLock(fakeWakeLock) whenever(controlsComponent.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) underTest = buildService { activity } fun setup() { whenever(kosmos.controlsComponent.getControlsListingController()) .thenReturn(Optional.of(kosmos.controlsListingController)) } @Test Loading @@ -90,9 +108,12 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { @Test fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() = testScope.runTest { underTest = buildService { null } val serviceWithNullActivity = buildService( mock<DreamServiceDelegate> { on { getActivity(underTest) } doReturn null } ) underTest.onAttachedToWindow() serviceWithNullActivity.onAttachedToWindow() verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any()) } Loading @@ -102,6 +123,7 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { underTest.onAttachedToWindow() assertThat(fakeWakeLock.isHeld).isTrue() } @Test fun testDetachWindow_wakeLockCanBeReleased() = testScope.runTest { Loading @@ -112,14 +134,60 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { assertThat(fakeWakeLock.isHeld).isFalse() } private fun buildService(activityProvider: DreamActivityProvider): HomeControlsDreamService = @Test fun testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakes() = testScope.runTest { whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(false) underTest.onAttachedToWindow() onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>()) verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) // Task fragment becomes empty onInfoChangedCallback.firstValue.invoke( mock<TaskFragmentInfo> { on { isEmpty } doReturn true } ) advanceUntilIdle() // Dream is finished and activity is not restarted verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) verify(dreamServiceDelegate, never()).wakeUp(any()) verify(dreamServiceDelegate).finish(any()) } @Test fun testRestartsActivityWhenRedirectingWakes() = testScope.runTest { whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(true) underTest.onAttachedToWindow() onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>()) verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) // Task fragment becomes empty onInfoChangedCallback.firstValue.invoke( mock<TaskFragmentInfo> { on { isEmpty } doReturn true } ) advanceUntilIdle() // Activity is restarted instead of finishing the dream. verify(taskFragmentComponent, times(2)).startActivityInTaskFragment(intentMatcher()) verify(dreamServiceDelegate).wakeUp(any()) verify(dreamServiceDelegate, never()).finish(any()) } private fun intentMatcher() = argThat<Intent> { getIntExtra(EXTRA_CONTROLS_SURFACE, CONTROLS_SURFACE_ACTIVITY_PANEL) == CONTROLS_SURFACE_DREAM } private fun buildService( activityProvider: DreamServiceDelegate = dreamServiceDelegate ): HomeControlsDreamService = with(kosmos) { return HomeControlsDreamService( controlsSettingsRepository = FakeControlsSettingsRepository(), taskFragmentFactory = taskFragmentComponentFactory, homeControlsComponentInteractor = homeControlsComponentInteractor, fakeWakeLockBuilder, dreamActivityProvider = activityProvider, wakeLockBuilder = fakeWakeLockBuilder, dreamServiceDelegate = activityProvider, bgDispatcher = testDispatcher, logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest") ) Loading packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +4 −4 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; import com.android.systemui.dreams.DreamOverlayService; import com.android.systemui.dreams.SystemDialogsCloser; import com.android.systemui.dreams.complication.dagger.ComplicationComponent; import com.android.systemui.dreams.homecontrols.DreamActivityProvider; import com.android.systemui.dreams.homecontrols.DreamActivityProviderImpl; import com.android.systemui.dreams.homecontrols.DreamServiceDelegate; import com.android.systemui.dreams.homecontrols.DreamServiceDelegateImpl; import com.android.systemui.dreams.homecontrols.HomeControlsDreamService; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.pipeline.shared.TileSpec; Loading Loading @@ -202,8 +202,8 @@ public interface DreamModule { } /** Provides activity for dream service */ /** Provides delegate to allow for testing of dream service */ @Binds DreamActivityProvider bindActivityProvider(DreamActivityProviderImpl impl); DreamServiceDelegate bindDreamDelegate(DreamServiceDelegateImpl impl); } packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProvider.kt→packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt +12 −5 Original line number Diff line number Diff line Loading @@ -18,10 +18,17 @@ package com.android.systemui.dreams.homecontrols import android.app.Activity import android.service.dreams.DreamService fun interface DreamActivityProvider { /** * Provides abstraction for getting the activity associated with a dream service, so that the * activity can be mocked in tests. */ /** Provides abstraction for [DreamService] methods, so they can be mocked in tests. */ interface DreamServiceDelegate { /** Wrapper for [DreamService.getActivity] which can be mocked in tests. */ fun getActivity(dreamService: DreamService): Activity? /** Wrapper for [DreamService.wakeUp] which can be mocked in tests. */ fun wakeUp(dreamService: DreamService) /** Wrapper for [DreamService.finish] which can be mocked in tests. */ fun finish(dreamService: DreamService) /** Wrapper for [DreamService.getRedirectWake] which can be mocked in tests. */ fun redirectWake(dreamService: DreamService): Boolean } packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt→packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt +13 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,20 @@ import android.app.Activity import android.service.dreams.DreamService import javax.inject.Inject class DreamActivityProviderImpl @Inject constructor() : DreamActivityProvider { class DreamServiceDelegateImpl @Inject constructor() : DreamServiceDelegate { override fun getActivity(dreamService: DreamService): Activity { return dreamService.activity } override fun finish(dreamService: DreamService) { dreamService.finish() } override fun wakeUp(dreamService: DreamService) { dreamService.wakeUp() } override fun redirectWake(dreamService: DreamService): Boolean { return dreamService.redirectWake } } Loading
core/java/android/service/dreams/DreamService.java +10 −1 Original line number Diff line number Diff line Loading @@ -1276,13 +1276,22 @@ public class DreamService extends Service implements Window.Callback { }); } /** * Whether or not wake requests will be redirected. * * @hide */ public boolean getRedirectWake() { return mOverlayConnection != null && mRedirectWake; } private void wakeUp(boolean fromSystem) { if (mDebug) { Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking + ", mFinished=" + mFinished); } if (!fromSystem && mOverlayConnection != null && mRedirectWake) { if (!fromSystem && getRedirectWake()) { mOverlayConnection.addConsumer(overlay -> { try { overlay.onWakeRequested(); Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt +103 −35 Original line number Diff line number Diff line Loading @@ -16,29 +16,40 @@ package com.android.systemui.dreams.homecontrols import android.app.Activity import android.content.Intent import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_ACTIVITY_PANEL import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_DREAM import android.service.controls.ControlsProviderService.EXTRA_CONTROLS_SURFACE import android.window.TaskFragmentInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.core.FakeLogBuffer.Factory.Companion.create import com.android.systemui.log.logcatLogBuffer import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.android.systemui.util.wakelock.WakeLockFake import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.argThat import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class HomeControlsDreamServiceTest : SysuiTestCase() { Loading @@ -46,30 +57,37 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder private lateinit var fakeWakeLock: WakeLockFake private val fakeWakeLock = WakeLockFake() private val fakeWakeLockBuilder by lazy { WakeLockFake.Builder(context).apply { setWakeLock(fakeWakeLock) } } @Mock private lateinit var taskFragmentComponentFactory: TaskFragmentComponent.Factory @Mock private lateinit var taskFragmentComponent: TaskFragmentComponent @Mock private lateinit var activity: Activity private val taskFragmentComponent = mock<TaskFragmentComponent>() private val activity = mock<Activity>() private val onCreateCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>() private val onInfoChangedCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>() private val hideCallback = argumentCaptor<() -> Unit>() private val dreamServiceDelegate = mock<DreamServiceDelegate> { on { getActivity(any()) } doReturn activity } private val taskFragmentComponentFactory = mock<TaskFragmentComponent.Factory> { on { create( activity = eq(activity), onCreateCallback = onCreateCallback.capture(), onInfoChangedCallback = onInfoChangedCallback.capture(), hide = hideCallback.capture(), ) } doReturn taskFragmentComponent } private lateinit var underTest: HomeControlsDreamService private val underTest: HomeControlsDreamService by lazy { buildService() } @Before fun setup() = with(kosmos) { MockitoAnnotations.initMocks(this@HomeControlsDreamServiceTest) whenever(taskFragmentComponentFactory.create(any(), any(), any(), any())) .thenReturn(taskFragmentComponent) fakeWakeLock = WakeLockFake() fakeWakeLockBuilder = WakeLockFake.Builder(context) fakeWakeLockBuilder.setWakeLock(fakeWakeLock) whenever(controlsComponent.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) underTest = buildService { activity } fun setup() { whenever(kosmos.controlsComponent.getControlsListingController()) .thenReturn(Optional.of(kosmos.controlsListingController)) } @Test Loading @@ -90,9 +108,12 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { @Test fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() = testScope.runTest { underTest = buildService { null } val serviceWithNullActivity = buildService( mock<DreamServiceDelegate> { on { getActivity(underTest) } doReturn null } ) underTest.onAttachedToWindow() serviceWithNullActivity.onAttachedToWindow() verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any()) } Loading @@ -102,6 +123,7 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { underTest.onAttachedToWindow() assertThat(fakeWakeLock.isHeld).isTrue() } @Test fun testDetachWindow_wakeLockCanBeReleased() = testScope.runTest { Loading @@ -112,14 +134,60 @@ class HomeControlsDreamServiceTest : SysuiTestCase() { assertThat(fakeWakeLock.isHeld).isFalse() } private fun buildService(activityProvider: DreamActivityProvider): HomeControlsDreamService = @Test fun testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakes() = testScope.runTest { whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(false) underTest.onAttachedToWindow() onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>()) verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) // Task fragment becomes empty onInfoChangedCallback.firstValue.invoke( mock<TaskFragmentInfo> { on { isEmpty } doReturn true } ) advanceUntilIdle() // Dream is finished and activity is not restarted verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) verify(dreamServiceDelegate, never()).wakeUp(any()) verify(dreamServiceDelegate).finish(any()) } @Test fun testRestartsActivityWhenRedirectingWakes() = testScope.runTest { whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(true) underTest.onAttachedToWindow() onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>()) verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher()) // Task fragment becomes empty onInfoChangedCallback.firstValue.invoke( mock<TaskFragmentInfo> { on { isEmpty } doReturn true } ) advanceUntilIdle() // Activity is restarted instead of finishing the dream. verify(taskFragmentComponent, times(2)).startActivityInTaskFragment(intentMatcher()) verify(dreamServiceDelegate).wakeUp(any()) verify(dreamServiceDelegate, never()).finish(any()) } private fun intentMatcher() = argThat<Intent> { getIntExtra(EXTRA_CONTROLS_SURFACE, CONTROLS_SURFACE_ACTIVITY_PANEL) == CONTROLS_SURFACE_DREAM } private fun buildService( activityProvider: DreamServiceDelegate = dreamServiceDelegate ): HomeControlsDreamService = with(kosmos) { return HomeControlsDreamService( controlsSettingsRepository = FakeControlsSettingsRepository(), taskFragmentFactory = taskFragmentComponentFactory, homeControlsComponentInteractor = homeControlsComponentInteractor, fakeWakeLockBuilder, dreamActivityProvider = activityProvider, wakeLockBuilder = fakeWakeLockBuilder, dreamServiceDelegate = activityProvider, bgDispatcher = testDispatcher, logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest") ) Loading
packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +4 −4 Original line number Diff line number Diff line Loading @@ -33,8 +33,8 @@ import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; import com.android.systemui.dreams.DreamOverlayService; import com.android.systemui.dreams.SystemDialogsCloser; import com.android.systemui.dreams.complication.dagger.ComplicationComponent; import com.android.systemui.dreams.homecontrols.DreamActivityProvider; import com.android.systemui.dreams.homecontrols.DreamActivityProviderImpl; import com.android.systemui.dreams.homecontrols.DreamServiceDelegate; import com.android.systemui.dreams.homecontrols.DreamServiceDelegateImpl; import com.android.systemui.dreams.homecontrols.HomeControlsDreamService; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.pipeline.shared.TileSpec; Loading Loading @@ -202,8 +202,8 @@ public interface DreamModule { } /** Provides activity for dream service */ /** Provides delegate to allow for testing of dream service */ @Binds DreamActivityProvider bindActivityProvider(DreamActivityProviderImpl impl); DreamServiceDelegate bindDreamDelegate(DreamServiceDelegateImpl impl); }
packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProvider.kt→packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt +12 −5 Original line number Diff line number Diff line Loading @@ -18,10 +18,17 @@ package com.android.systemui.dreams.homecontrols import android.app.Activity import android.service.dreams.DreamService fun interface DreamActivityProvider { /** * Provides abstraction for getting the activity associated with a dream service, so that the * activity can be mocked in tests. */ /** Provides abstraction for [DreamService] methods, so they can be mocked in tests. */ interface DreamServiceDelegate { /** Wrapper for [DreamService.getActivity] which can be mocked in tests. */ fun getActivity(dreamService: DreamService): Activity? /** Wrapper for [DreamService.wakeUp] which can be mocked in tests. */ fun wakeUp(dreamService: DreamService) /** Wrapper for [DreamService.finish] which can be mocked in tests. */ fun finish(dreamService: DreamService) /** Wrapper for [DreamService.getRedirectWake] which can be mocked in tests. */ fun redirectWake(dreamService: DreamService): Boolean }
packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt→packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt +13 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,20 @@ import android.app.Activity import android.service.dreams.DreamService import javax.inject.Inject class DreamActivityProviderImpl @Inject constructor() : DreamActivityProvider { class DreamServiceDelegateImpl @Inject constructor() : DreamServiceDelegate { override fun getActivity(dreamService: DreamService): Activity { return dreamService.activity } override fun finish(dreamService: DreamService) { dreamService.finish() } override fun wakeUp(dreamService: DreamService) { dreamService.wakeUp() } override fun redirectWake(dreamService: DreamService): Boolean { return dreamService.redirectWake } }