1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display.mode
18 
19 import android.content.Context
20 import android.content.ContextWrapper
21 import android.provider.Settings
22 import android.util.SparseArray
23 import android.view.Display
24 import android.view.SurfaceControl.RefreshRateRange
25 import android.view.SurfaceControl.RefreshRateRanges
26 import androidx.test.core.app.ApplicationProvider
27 import androidx.test.filters.SmallTest
28 import com.android.internal.util.test.FakeSettingsProvider
29 import com.android.server.display.DisplayDeviceConfig
30 import com.android.server.display.config.RefreshRateData
31 import com.android.server.display.config.SupportedModeData
32 import com.android.server.display.feature.DisplayManagerFlags
33 import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
34 import com.android.server.display.mode.SupportedRefreshRatesVote.RefreshRates
35 import com.android.server.testutils.TestHandler
36 import com.google.common.truth.Truth.assertThat
37 import com.google.common.truth.Truth.assertWithMessage
38 import com.google.testing.junit.testparameterinjector.TestParameter
39 import com.google.testing.junit.testparameterinjector.TestParameterInjector
40 import org.junit.Before
41 import org.junit.Rule
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import org.mockito.Mockito
45 import org.mockito.junit.MockitoJUnit
46 import org.mockito.kotlin.mock
47 import org.mockito.kotlin.whenever
48 
49 private val RANGE_NO_LIMIT = RefreshRateRange(0f, Float.POSITIVE_INFINITY)
50 private val RANGE_0_60 = RefreshRateRange(0f, 60f)
51 private val RANGE_0_90 = RefreshRateRange(0f, 90f)
52 private val RANGE_0_120 = RefreshRateRange(0f, 120f)
53 private val RANGE_60_90 = RefreshRateRange(60f, 90f)
54 private val RANGE_60_120 = RefreshRateRange(60f, 120f)
55 private val RANGE_60_INF = RefreshRateRange(60f, Float.POSITIVE_INFINITY)
56 private val RANGE_90_90 = RefreshRateRange(90f, 90f)
57 private val RANGE_90_120 = RefreshRateRange(90f, 120f)
58 private val RANGE_90_INF = RefreshRateRange(90f, Float.POSITIVE_INFINITY)
59 
60 private val RANGES_NO_LIMIT = RefreshRateRanges(RANGE_NO_LIMIT, RANGE_NO_LIMIT)
61 private val RANGES_NO_LIMIT_60 = RefreshRateRanges(RANGE_NO_LIMIT, RANGE_0_60)
62 private val RANGES_NO_LIMIT_90 = RefreshRateRanges(RANGE_NO_LIMIT, RANGE_0_90)
63 private val RANGES_NO_LIMIT_120 = RefreshRateRanges(RANGE_NO_LIMIT, RANGE_0_120)
64 private val RANGES_90 = RefreshRateRanges(RANGE_0_90, RANGE_0_90)
65 private val RANGES_120 = RefreshRateRanges(RANGE_0_120, RANGE_0_120)
66 private val RANGES_90_60 = RefreshRateRanges(RANGE_0_90, RANGE_0_60)
67 private val RANGES_90TO90 = RefreshRateRanges(RANGE_90_90, RANGE_90_90)
68 private val RANGES_90TO120 = RefreshRateRanges(RANGE_90_120, RANGE_90_120)
69 private val RANGES_60TO120_60TO90 = RefreshRateRanges(RANGE_60_120, RANGE_60_90)
70 private val RANGES_MIN90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_INF)
71 private val RANGES_MIN90_90TO120 = RefreshRateRanges(RANGE_90_INF, RANGE_90_120)
72 private val RANGES_MIN60_60TO90 = RefreshRateRanges(RANGE_60_INF, RANGE_60_90)
73 private val RANGES_MIN90_90TO90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_90)
74 
75 private val LOW_POWER_GLOBAL_VOTE = Vote.forRenderFrameRates(0f, 60f)
76 private val LOW_POWER_REFRESH_RATE_DATA = createRefreshRateData(
77     lowPowerSupportedModes = listOf(SupportedModeData(60f, 60f), SupportedModeData(60f, 240f)))
78 private val LOW_POWER_EMPTY_REFRESH_RATE_DATA = createRefreshRateData()
79 private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote(
80     listOf(RefreshRates(60f, 60f), RefreshRates(60f, 240f)))
81 
82 @SmallTest
83 @RunWith(TestParameterInjector::class)
84 class SettingsObserverTest {
85     @get:Rule
86     val mockitoRule = MockitoJUnit.rule()
87 
88     @get:Rule
89     val settingsProviderRule = FakeSettingsProvider.rule()
90 
91     private lateinit var spyContext: Context
92     private val mockInjector = mock<DisplayModeDirector.Injector>()
93     private val mockFlags = mock<DisplayManagerFlags>()
94     private val mockDeviceConfig = mock<DisplayDeviceConfig>()
95     private val mockDisplayDeviceConfigProvider = mock<DisplayDeviceConfigProvider>()
96 
97     private val testHandler = TestHandler(null)
98 
99     @Before
setUpnull100     fun setUp() {
101         spyContext = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
102     }
103 
104     @Test
test low power modenull105     fun `test low power mode`(@TestParameter testCase: LowPowerTestCase) {
106         whenever(mockFlags.isVsyncLowPowerVoteEnabled).thenReturn(testCase.vsyncLowPowerVoteEnabled)
107         whenever(spyContext.contentResolver)
108                 .thenReturn(settingsProviderRule.mockContentResolver(null))
109         val lowPowerModeSetting = if (testCase.lowPowerModeEnabled) 1 else 0
110         Settings.Global.putInt(
111                 spyContext.contentResolver, Settings.Global.LOW_POWER_MODE, lowPowerModeSetting)
112 
113         val displayModeDirector = DisplayModeDirector(
114                 spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
115         val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
116         whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData)
117         ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
118         displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
119         val settingsObserver = displayModeDirector.SettingsObserver(
120                 spyContext, testHandler, mockFlags)
121 
122         settingsObserver.onChange(
123                 false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
124 
125         assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
126                 Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE)).isEqualTo(testCase.globalVote)
127         assertThat(displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
128             Vote.PRIORITY_LOW_POWER_MODE_MODES)).isEqualTo(testCase.displayVote)
129     }
130 
131     enum class LowPowerTestCase(
132         val refreshRateData: RefreshRateData,
133         val vsyncLowPowerVoteEnabled: Boolean,
134         val lowPowerModeEnabled: Boolean,
135         internal val globalVote: Vote?,
136         internal val displayVote: Vote?
137     ) {
138         ALL_ENABLED(LOW_POWER_REFRESH_RATE_DATA, true, true,
139             LOW_POWER_GLOBAL_VOTE, EXPECTED_SUPPORTED_MODES_VOTE),
140         LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, true, false,
141             null, null),
142         EMPTY_REFRESH_LOW_POWER_ON(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, true,
143             LOW_POWER_GLOBAL_VOTE, null),
144         EMPTY_REFRESH__LOW_POWER_OFF(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, false,
145             null, null),
146         VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(LOW_POWER_REFRESH_RATE_DATA, false, true,
147             LOW_POWER_GLOBAL_VOTE, null),
148         VSYNC_VOTE_DISABLED_LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, false, false,
149             null, null),
150     }
151 
152     @Test
test settings refresh ratesnull153     fun `test settings refresh rates`(@TestParameter testCase: SettingsRefreshRateTestCase) {
154         whenever(mockFlags.isPeakRefreshRatePhysicalLimitEnabled)
155                 .thenReturn(testCase.peakRefreshRatePhysicalLimitEnabled)
156 
157         val displayModeDirector = DisplayModeDirector(
158             spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
159 
160         val modes = arrayOf(
161             Display.Mode(1, 1000, 1000, 60f),
162             Display.Mode(2, 1000, 1000, 90f),
163             Display.Mode(3, 1000, 1000, 120f)
164         )
165         displayModeDirector.injectSupportedModesByDisplay(SparseArray<Array<Display.Mode>>().apply {
166             append(Display.DEFAULT_DISPLAY, modes)
167         })
168         displayModeDirector.injectDefaultModeByDisplay(SparseArray<Display.Mode>().apply {
169             append(Display.DEFAULT_DISPLAY, modes[0])
170         })
171 
172         val specs = displayModeDirector.getDesiredDisplayModeSpecsWithInjectedFpsSettings(
173             testCase.minRefreshRate, testCase.peakRefreshRate, testCase.defaultRefreshRate)
174 
175         assertWithMessage("Primary RefreshRateRanges: ")
176                 .that(specs.primary).isEqualTo(testCase.expectedPrimaryRefreshRateRanges)
177         assertWithMessage("App RefreshRateRanges: ")
178                 .that(specs.appRequest).isEqualTo(testCase.expectedAppRefreshRateRanges)
179     }
180 
181     /**
182      * Votes considered:
183      * priority: PRIORITY_USER_SETTING_PEAK_REFRESH_RATE (also used for appRanged)
184      * condition: peakRefreshRatePhysicalLimitEnabled, peakRR > 0
185      * vote: physical(minRR, peakRR)
186      *
187      * priority: PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE (also used for appRanged)
188      * condition: peakRR > 0
189      * vote: render(minRR, peakRR)
190      *
191      * priority: PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE
192      * condition: -
193      * vote: render(minRR, INF)
194      *
195      * priority: PRIORITY_DEFAULT_RENDER_FRAME_RATE
196      * condition: defaultRR > 0
197      * vote: render(0, defaultRR)
198      *
199      * 0 considered not set
200      *
201      * For this test:
202      * primary physical rate:
203      *          (minRR, peakRefreshRatePhysicalLimitEnabled ? max(minRR, peakRR) : INF)
204      * primary render rate : (minRR, min(defaultRR, max(minRR, peakRR)))
205      *
206      * app physical rate: (0, peakRefreshRatePhysicalLimitEnabled ? max(minRR, peakRR) : INF)
207      * app render rate: (0, max(minRR, peakRR))
208      */
209     enum class SettingsRefreshRateTestCase(
210         val minRefreshRate: Float,
211         val peakRefreshRate: Float,
212         val defaultRefreshRate: Float,
213         val peakRefreshRatePhysicalLimitEnabled: Boolean,
214         val expectedPrimaryRefreshRateRanges: RefreshRateRanges,
215         val expectedAppRefreshRateRanges: RefreshRateRanges,
216     ) {
217         NO_LIMIT(0f, 0f, 0f, false, RANGES_NO_LIMIT, RANGES_NO_LIMIT),
218         NO_LIMIT_WITH_PHYSICAL_RR(0f, 0f, 0f, true, RANGES_NO_LIMIT, RANGES_NO_LIMIT),
219 
220         LIMITS_0_0_90(0f, 0f, 90f, false, RANGES_NO_LIMIT_90, RANGES_NO_LIMIT),
221         LIMITS_0_0_90_WITH_PHYSICAL_RR(0f, 0f, 90f, true, RANGES_NO_LIMIT_90, RANGES_NO_LIMIT),
222 
223         LIMITS_0_90_0(0f, 90f, 0f, false, RANGES_NO_LIMIT_90, RANGES_NO_LIMIT_90),
224         LIMITS_0_90_0_WITH_PHYSICAL_RR(0f, 90f, 0f, true, RANGES_90, RANGES_90),
225 
226         LIMITS_0_90_60(0f, 90f, 60f, false, RANGES_NO_LIMIT_60, RANGES_NO_LIMIT_90),
227         LIMITS_0_90_60_WITH_PHYSICAL_RR(0f, 90f, 60f, true, RANGES_90_60, RANGES_90),
228 
229         LIMITS_0_90_120(0f, 90f, 120f, false, RANGES_NO_LIMIT_90, RANGES_NO_LIMIT_90),
230         LIMITS_0_90_120_WITH_PHYSICAL_RR(0f, 90f, 120f, true, RANGES_90, RANGES_90),
231 
232         LIMITS_90_0_0(90f, 0f, 0f, false, RANGES_MIN90, RANGES_NO_LIMIT),
233         LIMITS_90_0_0_WITH_PHYSICAL_RR(90f, 0f, 0f, true, RANGES_MIN90, RANGES_NO_LIMIT),
234 
235         LIMITS_90_0_120(90f, 0f, 120f, false, RANGES_MIN90_90TO120, RANGES_NO_LIMIT),
236         LIMITS_90_0_120_WITH_PHYSICAL_RR(90f,
237             0f,
238             120f,
239             true,
240             RANGES_MIN90_90TO120,
241             RANGES_NO_LIMIT),
242 
243         LIMITS_90_0_60(90f, 0f, 60f, false, RANGES_MIN90, RANGES_NO_LIMIT),
244         LIMITS_90_0_60_WITH_PHYSICAL_RR(90f, 0f, 60f, true, RANGES_MIN90, RANGES_NO_LIMIT),
245 
246         LIMITS_90_120_0(90f, 120f, 0f, false, RANGES_MIN90_90TO120, RANGES_NO_LIMIT_120),
247         LIMITS_90_120_0_WITH_PHYSICAL_RR(90f, 120f, 0f, true, RANGES_90TO120, RANGES_120),
248 
249         LIMITS_90_60_0(90f, 60f, 0f, false, RANGES_MIN90_90TO90, RANGES_NO_LIMIT_90),
250         LIMITS_90_60_0_WITH_PHYSICAL_RR(90f, 60f, 0f, true, RANGES_90TO90, RANGES_90),
251 
252         LIMITS_60_120_90(60f, 120f, 90f, false, RANGES_MIN60_60TO90, RANGES_NO_LIMIT_120),
253         LIMITS_60_120_90_WITH_PHYSICAL_RR(60f, 120f, 90f, true, RANGES_60TO120_60TO90, RANGES_120),
254     }
255 }