1 /*
2  * Copyright (C) 2019 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 static android.hardware.display.DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA;
20 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
21 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
22 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
23 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
24 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT;
25 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
26 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
27 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
28 import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
29 
30 import static com.android.server.display.mode.Vote.INVALID_SIZE;
31 
32 import static com.google.common.truth.Truth.assertThat;
33 
34 import static org.junit.Assert.assertArrayEquals;
35 import static org.junit.Assert.assertEquals;
36 import static org.junit.Assert.assertNotNull;
37 import static org.junit.Assert.assertNull;
38 import static org.junit.Assert.assertTrue;
39 import static org.mockito.ArgumentMatchers.anyInt;
40 import static org.mockito.ArgumentMatchers.eq;
41 import static org.mockito.Mockito.any;
42 import static org.mockito.Mockito.atLeastOnce;
43 import static org.mockito.Mockito.doAnswer;
44 import static org.mockito.Mockito.mock;
45 import static org.mockito.Mockito.never;
46 import static org.mockito.Mockito.spy;
47 import static org.mockito.Mockito.verify;
48 import static org.mockito.Mockito.when;
49 import static org.mockito.internal.verification.VerificationModeFactory.times;
50 
51 import android.annotation.NonNull;
52 import android.content.ContentResolver;
53 import android.content.Context;
54 import android.content.ContextWrapper;
55 import android.content.res.Resources;
56 import android.database.ContentObserver;
57 import android.hardware.Sensor;
58 import android.hardware.SensorEventListener;
59 import android.hardware.SensorManager;
60 import android.hardware.display.BrightnessInfo;
61 import android.hardware.display.DisplayManager;
62 import android.hardware.display.DisplayManager.DisplayListener;
63 import android.hardware.display.DisplayManagerGlobal;
64 import android.hardware.display.DisplayManagerInternal;
65 import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
66 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
67 import android.os.Handler;
68 import android.os.IThermalEventListener;
69 import android.os.Looper;
70 import android.os.RemoteException;
71 import android.os.Temperature;
72 import android.provider.DeviceConfig;
73 import android.provider.Settings;
74 import android.test.mock.MockContentResolver;
75 import android.util.Slog;
76 import android.util.SparseArray;
77 import android.util.TypedValue;
78 import android.view.Display;
79 import android.view.DisplayInfo;
80 import android.view.SurfaceControl.RefreshRateRange;
81 import android.view.SurfaceControl.RefreshRateRanges;
82 
83 import androidx.test.core.app.ApplicationProvider;
84 import androidx.test.filters.SmallTest;
85 
86 import com.android.internal.R;
87 import com.android.internal.display.BrightnessSynchronizer;
88 import com.android.internal.os.BackgroundThread;
89 import com.android.internal.util.Preconditions;
90 import com.android.internal.util.test.FakeSettingsProvider;
91 import com.android.internal.util.test.FakeSettingsProviderRule;
92 import com.android.server.LocalServices;
93 import com.android.server.display.DisplayDeviceConfig;
94 import com.android.server.display.TestUtils;
95 import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
96 import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
97 import com.android.server.sensors.SensorManagerInternal;
98 import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
99 import com.android.server.statusbar.StatusBarManagerInternal;
100 import com.android.server.testutils.FakeDeviceConfigInterface;
101 
102 import org.junit.Before;
103 import org.junit.Rule;
104 import org.junit.Test;
105 import org.junit.runner.RunWith;
106 import org.mockito.ArgumentCaptor;
107 import org.mockito.Mock;
108 import org.mockito.Mockito;
109 import org.mockito.MockitoAnnotations;
110 import org.mockito.stubbing.Answer;
111 
112 import java.util.ArrayList;
113 import java.util.Arrays;
114 import java.util.List;
115 import java.util.concurrent.Executor;
116 import java.util.concurrent.TimeUnit;
117 import java.util.stream.Collectors;
118 
119 import junitparams.JUnitParamsRunner;
120 import junitparams.Parameters;
121 
122 @SmallTest
123 @RunWith(JUnitParamsRunner.class)
124 public class DisplayModeDirectorTest {
125     // The tolerance within which we consider something approximately equals.
126     private static final String TAG = "DisplayModeDirectorTest";
127     private static final boolean DEBUG = false;
128     private static final float FLOAT_TOLERANCE = 0.01f;
129     private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
130     private static final int MODE_ID = 1;
131     private static final float TRANSITION_POINT = 0.763f;
132 
133     private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
134 
135     private Context mContext;
136     private FakesInjector mInjector;
137     private Handler mHandler;
138     @Rule
139     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
140     @Mock
141     public StatusBarManagerInternal mStatusBarMock;
142     @Mock
143     public SensorManagerInternal mSensorManagerInternalMock;
144     @Mock
145     public DisplayManagerInternal mDisplayManagerInternalMock;
146 
147     @Before
setUp()148     public void setUp() throws Exception {
149         MockitoAnnotations.initMocks(this);
150         mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
151         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
152         when(mContext.getContentResolver()).thenReturn(resolver);
153         mInjector = spy(new FakesInjector());
154         mHandler = new Handler(Looper.getMainLooper());
155 
156         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
157         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarMock);
158         LocalServices.removeServiceForTest(SensorManagerInternal.class);
159         LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
160         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
161         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
162     }
163 
createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId)164     private DisplayModeDirector createDirectorFromRefreshRateArray(
165             float[] refreshRates, int baseModeId) {
166         return createDirectorFromRefreshRateArray(refreshRates, baseModeId, refreshRates[0]);
167     }
168 
createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId, float defaultRefreshRate)169     private DisplayModeDirector createDirectorFromRefreshRateArray(
170             float[] refreshRates, int baseModeId, float defaultRefreshRate) {
171         Display.Mode[] modes = new Display.Mode[refreshRates.length];
172         Display.Mode defaultMode = null;
173         for (int i = 0; i < refreshRates.length; i++) {
174             modes[i] = new Display.Mode(
175                     /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
176             if (refreshRates[i] == defaultRefreshRate) {
177                 defaultMode = modes[i];
178             }
179         }
180         assertThat(defaultMode).isNotNull();
181         return createDirectorFromModeArray(modes, defaultMode);
182     }
183 
createDirectorFromModeArray(Display.Mode[] modes, Display.Mode defaultMode)184     private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
185             Display.Mode defaultMode) {
186         DisplayModeDirector director =
187                 new DisplayModeDirector(mContext, mHandler, mInjector);
188         director.setLoggingEnabled(true);
189         SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
190         supportedModesByDisplay.put(DISPLAY_ID, modes);
191         director.injectSupportedModesByDisplay(supportedModesByDisplay);
192         SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
193         defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
194         director.injectDefaultModeByDisplay(defaultModesByDisplay);
195         return director;
196     }
197 
createDirectorFromFpsRange(int minFps, int maxFps)198     private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) {
199         int numRefreshRates = maxFps - minFps + 1;
200         float[] refreshRates = new float[numRefreshRates];
201         for (int i = 0; i < numRefreshRates; i++) {
202             refreshRates[i] = minFps + i;
203         }
204         return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps,
205                 /*defaultRefreshRate=*/minFps);
206     }
207 
208     @Test
testDisplayModeVoting()209     public void testDisplayModeVoting() {
210         // With no votes present, DisplayModeDirector should allow any refresh rate.
211         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
212         DesiredDisplayModeSpecs modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
213         assertThat(modeSpecs.baseModeId).isEqualTo(60);
214         assertThat(modeSpecs.primary.physical.min).isEqualTo(0f);
215         assertThat(modeSpecs.primary.physical.max).isEqualTo(Float.POSITIVE_INFINITY);
216         assertThat(modeSpecs.primary.render.min).isEqualTo(0f);
217         assertThat(modeSpecs.primary.render.max).isEqualTo(Float.POSITIVE_INFINITY);
218         assertThat(modeSpecs.appRequest.physical.min).isEqualTo(0f);
219         assertThat(modeSpecs.appRequest.physical.max).isEqualTo(Float.POSITIVE_INFINITY);
220         assertThat(modeSpecs.appRequest.render.min).isEqualTo(0f);
221         assertThat(modeSpecs.appRequest.render.max).isEqualTo(Float.POSITIVE_INFINITY);
222 
223         int numPriorities =  Vote.MAX_PRIORITY - Vote.MIN_PRIORITY + 1;
224 
225         // Ensure vote priority works as expected. As we add new votes with higher priority, they
226         // should take precedence over lower priority votes.
227         {
228             int minFps = 60;
229             int maxFps = 90;
230             director = createDirectorFromFpsRange(60, 90);
231             assertTrue(2 * numPriorities < maxFps - minFps + 1);
232             SparseArray<Vote> votes = new SparseArray<>();
233             SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
234             votesByDisplay.put(DISPLAY_ID, votes);
235             for (int i = 0; i < numPriorities; i++) {
236                 int priority = Vote.MIN_PRIORITY + i;
237                 votes.put(priority, Vote.forPhysicalRefreshRates(minFps + i, maxFps - i));
238                 director.injectVotesByDisplay(votesByDisplay);
239                 modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
240                 assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i);
241                 assertThat(modeSpecs.primary.physical.min)
242                         .isEqualTo((float) (minFps + i));
243                 assertThat(modeSpecs.primary.physical.max)
244                         .isEqualTo((float) (maxFps - i));
245                 assertThat(modeSpecs.primary.render.min).isZero();
246                 assertThat(modeSpecs.primary.render.max)
247                         .isEqualTo((float) (maxFps - i));
248                 if (priority >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF) {
249                     assertThat(modeSpecs.appRequest.physical.min)
250                             .isEqualTo((float) (minFps + i));
251                     assertThat(modeSpecs.appRequest.physical.max)
252                             .isEqualTo((float) (maxFps - i));
253                     assertThat(modeSpecs.appRequest.render.min).isZero();
254                     assertThat(modeSpecs.appRequest.render.max).isEqualTo(
255                             (float) (maxFps - i));
256                 } else {
257                     assertThat(modeSpecs.appRequest.physical.min).isZero();
258                     assertThat(modeSpecs.appRequest.physical.max).isPositiveInfinity();
259                     assertThat(modeSpecs.appRequest.render.min).isZero();
260                     assertThat(modeSpecs.appRequest.render.max).isPositiveInfinity();
261                 }
262 
263             }
264         }
265 
266         // Ensure lower priority votes are able to influence the final decision, even in the
267         // presence of higher priority votes.
268         {
269             assertTrue(numPriorities >= 2);
270             director = createDirectorFromFpsRange(60, 90);
271             SparseArray<Vote> votes = new SparseArray<>();
272             SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
273             votesByDisplay.put(DISPLAY_ID, votes);
274             votes.put(Vote.MAX_PRIORITY, Vote.forPhysicalRefreshRates(65, 85));
275             votes.put(Vote.MIN_PRIORITY, Vote.forPhysicalRefreshRates(70, 80));
276             director.injectVotesByDisplay(votesByDisplay);
277             modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
278             assertThat(modeSpecs.baseModeId).isEqualTo(70);
279             assertThat(modeSpecs.primary.physical.min).isEqualTo(70f);
280             assertThat(modeSpecs.primary.physical.max).isEqualTo(80f);
281         }
282     }
283 
284     @Test
testVotingWithFloatingPointErrors()285     public void testVotingWithFloatingPointErrors() {
286         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
287         SparseArray<Vote> votes = new SparseArray<>();
288         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
289         votesByDisplay.put(DISPLAY_ID, votes);
290         float error = FLOAT_TOLERANCE / 4;
291         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
292                 Vote.forRenderFrameRates(0, 60));
293         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE,
294                 Vote.forPhysicalRefreshRates(60 + error, 60 + error));
295         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
296                 Vote.forPhysicalRefreshRates(60 - error, 60 - error));
297         director.injectVotesByDisplay(votesByDisplay);
298         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
299 
300         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
301         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
302         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
303 
304         votes.clear();
305         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
306                 Vote.forRenderFrameRates(0, 60 - error));
307         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
308                 Vote.forRenderFrameRates(60 + error, Float.POSITIVE_INFINITY));
309         director.injectVotesByDisplay(votesByDisplay);
310         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
311 
312         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
313         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
314         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
315     }
316 
317     @Test
testFlickerHasLowerPriorityThanUserAndRangeIsSingle()318     public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() {
319         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
320                 < Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
321         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
322                 < Vote.PRIORITY_APP_REQUEST_SIZE);
323 
324         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
325                 > Vote.PRIORITY_LOW_POWER_MODE);
326 
327         Display.Mode[] modes = new Display.Mode[4];
328         modes[0] = new Display.Mode(
329                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
330         modes[1] = new Display.Mode(
331                 /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
332         modes[2] = new Display.Mode(
333                 /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
334         modes[3] = new Display.Mode(
335                 /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
336 
337         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
338         SparseArray<Vote> votes = new SparseArray<>();
339         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
340         votesByDisplay.put(DISPLAY_ID, votes);
341         Display.Mode appRequestedMode = modes[1];
342         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
343                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
344         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
345                 appRequestedMode.getPhysicalHeight()));
346         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60));
347         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
348         director.injectVotesByDisplay(votesByDisplay);
349         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
350         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
351         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
352         assertThat(desiredSpecs.primary.physical.max)
353                 .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min);
354 
355         votes.clear();
356         appRequestedMode = modes[3];
357         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
358                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
359         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
360                 appRequestedMode.getPhysicalHeight()));
361         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(90, 90));
362         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
363         director.injectVotesByDisplay(votesByDisplay);
364         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
365         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
366         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
367         assertThat(desiredSpecs.primary.physical.max)
368                 .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min);
369 
370         votes.clear();
371         appRequestedMode = modes[3];
372         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
373                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
374         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
375                 appRequestedMode.getPhysicalHeight()));
376         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60));
377         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
378         director.injectVotesByDisplay(votesByDisplay);
379         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
380         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
381         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
382         assertThat(desiredSpecs.primary.physical.max)
383                 .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min);
384 
385         votes.clear();
386         appRequestedMode = modes[1];
387         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
388                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
389         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
390                 appRequestedMode.getPhysicalHeight()));
391         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(90, 90));
392         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
393         director.injectVotesByDisplay(votesByDisplay);
394         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
395         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
396         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
397         assertThat(desiredSpecs.primary.physical.max)
398                 .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primary.physical.min);
399     }
400 
401     @Test
402     public void testLPMHasHigherPriorityThanUser() {
403         assertTrue(Vote.PRIORITY_LOW_POWER_MODE
404                 > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
405         assertTrue(Vote.PRIORITY_LOW_POWER_MODE
406                 > Vote.PRIORITY_APP_REQUEST_SIZE);
407 
408         Display.Mode[] modes = new Display.Mode[4];
409         modes[0] = new Display.Mode(
410                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
411         modes[1] = new Display.Mode(
412                 /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
413         modes[2] = new Display.Mode(
414                 /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
415         modes[3] = new Display.Mode(
416                 /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
417 
418         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
419         SparseArray<Vote> votes = new SparseArray<>();
420         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
421         votesByDisplay.put(DISPLAY_ID, votes);
422         Display.Mode appRequestedMode = modes[1];
423         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
424                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
425         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
426                 appRequestedMode.getPhysicalHeight()));
427         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
428         director.injectVotesByDisplay(votesByDisplay);
429         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
430         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
431         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
432         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
433         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
434         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
435 
436         votes.clear();
437         appRequestedMode = modes[3];
438         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
439                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
440         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
441                 appRequestedMode.getPhysicalHeight()));
442         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
443         director.injectVotesByDisplay(votesByDisplay);
444         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
445         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
446         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
447         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
448         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
449         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
450 
451         votes.clear();
452         appRequestedMode = modes[3];
453         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
454                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
455         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
456                 appRequestedMode.getPhysicalHeight()));
457         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
458         director.injectVotesByDisplay(votesByDisplay);
459         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
460         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
461         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
462         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
463         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
464         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
465 
466         votes.clear();
467         appRequestedMode = modes[1];
468         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
469                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
470         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
471                 appRequestedMode.getPhysicalHeight()));
472         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
473         director.injectVotesByDisplay(votesByDisplay);
474         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
475         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
476         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
477         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
478         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
479         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
480     }
481 
482     @Test
testAppRequestRefreshRateRange()483     public void testAppRequestRefreshRateRange() {
484         // Confirm that the app request range doesn't include flicker or min refresh rate settings,
485         // but does include everything else.
486         assertTrue(
487                 Vote.PRIORITY_FLICKER_REFRESH_RATE
488                         < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
489         assertTrue(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE
490                 < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
491         assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE
492                 >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
493 
494         Display.Mode[] modes = new Display.Mode[3];
495         modes[0] = new Display.Mode(
496                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
497         modes[1] = new Display.Mode(
498                 /*modeId=*/75, /*width=*/2000, /*height=*/2000, 75);
499         modes[2] = new Display.Mode(
500                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
501 
502         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
503         SparseArray<Vote> votes = new SparseArray<>();
504         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
505         votesByDisplay.put(DISPLAY_ID, votes);
506         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60));
507         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
508         director.injectVotesByDisplay(votesByDisplay);
509         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
510         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
511         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
512         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
513         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
514         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
515 
516         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
517                 Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
518         director.injectVotesByDisplay(votesByDisplay);
519         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
520         assertThat(desiredSpecs.baseModeId).isEqualTo(90);
521         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
522         assertThat(desiredSpecs.primary.physical.max).isAtLeast(90f);
523         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
524         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
525 
526         Display.Mode appRequestedMode = modes[1];
527         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
528                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
529         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
530                 appRequestedMode.getPhysicalHeight()));
531         director.injectVotesByDisplay(votesByDisplay);
532         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
533         assertThat(desiredSpecs.baseModeId).isEqualTo(75);
534         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
535         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75);
536         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
537         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
538     }
539 
540     void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps,
541             float peakFps, float defaultFps, RefreshRateRanges primary,
542             RefreshRateRanges appRequest) {
543         DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(
544                 minFps, peakFps, defaultFps);
545         assertThat(specs.primary).isEqualTo(primary);
546         assertThat(specs.appRequest).isEqualTo(appRequest);
547     }
548 
549     @Test
550     public void testSpecsFromRefreshRateSettings() {
551         // Confirm that, with varying settings for min, peak, and default refresh rate,
552         // DesiredDisplayModeSpecs is calculated correctly.
553         float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f};
554         DisplayModeDirector director =
555                 createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0);
556 
557         float inf = Float.POSITIVE_INFINITY;
558         RefreshRateRange rangeAll = new RefreshRateRange(0, inf);
559         RefreshRateRange range0to60 = new RefreshRateRange(0, 60);
560         RefreshRateRange range0to90 = new RefreshRateRange(0, 90);
561         RefreshRateRange range0to120 = new RefreshRateRange(0, 120);
562         RefreshRateRange range60to90 = new RefreshRateRange(60, 90);
563         RefreshRateRange range90to90 = new RefreshRateRange(90, 90);
564         RefreshRateRange range90to120 = new RefreshRateRange(90, 120);
565         RefreshRateRange range60toInf = new RefreshRateRange(60, inf);
566         RefreshRateRange range90toInf = new RefreshRateRange(90, inf);
567 
568         RefreshRateRanges frameRateAll = new RefreshRateRanges(rangeAll, rangeAll);
569         RefreshRateRanges frameRate90toInf = new RefreshRateRanges(range90toInf, range90toInf);
570         RefreshRateRanges frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
571         RefreshRateRanges frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
572         RefreshRateRanges frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
573         RefreshRateRanges frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
574         RefreshRateRanges frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
575         RefreshRateRanges frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
576 
577         verifySpecsWithRefreshRateSettings(director, 0, 0, 0, frameRateAll, frameRateAll);
578         verifySpecsWithRefreshRateSettings(director, 0, 0, 90, frameRate0to90, frameRateAll);
579         verifySpecsWithRefreshRateSettings(director, 0, 90, 0, frameRate0to90, frameRate0to90);
580         verifySpecsWithRefreshRateSettings(director, 0, 90, 60, frameRate0to60, frameRate0to90);
581         verifySpecsWithRefreshRateSettings(director, 0, 90, 120, frameRate0to90,
582                 frameRate0to90);
583         verifySpecsWithRefreshRateSettings(director, 90, 0, 0, frameRate90toInf, frameRateAll);
584         verifySpecsWithRefreshRateSettings(director, 90, 0, 120, frameRate90to120,
585                 frameRateAll);
586         verifySpecsWithRefreshRateSettings(director, 90, 0, 60, frameRate90toInf, frameRateAll);
587         verifySpecsWithRefreshRateSettings(director, 90, 120, 0, frameRate90to120,
588                 frameRate0to120);
589         verifySpecsWithRefreshRateSettings(director, 90, 60, 0, frameRate90to90,
590                 frameRate0to90);
591         verifySpecsWithRefreshRateSettings(director, 60, 120, 90, frameRate60to90,
592                 frameRate0to120);
593     }
594 
595     void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps,
596             float defaultFps, float brightnessObserverMin, float brightnessObserverMax) {
597         BrightnessObserver brightnessObserver = mock(BrightnessObserver.class);
598         director.injectBrightnessObserver(brightnessObserver);
599         director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(minFps, peakFps, defaultFps);
600         verify(brightnessObserver)
601                 .onRefreshRateSettingChangedLocked(brightnessObserverMin, brightnessObserverMax);
602     }
603 
604     @Test
605     public void testBrightnessObserverCallWithRefreshRateSettings() {
606         // Confirm that, with varying settings for min, peak, and default refresh rate, we make the
607         // correct call to the brightness observer.
608         float[] refreshRates = {60.f, 90.f, 120.f};
609         DisplayModeDirector director =
610                 createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0);
611         verifyBrightnessObserverCall(director, 0, 0, 0, 0, 0);
612         verifyBrightnessObserverCall(director, 0, 0, 90, 0, 90);
613         verifyBrightnessObserverCall(director, 0, 90, 0, 0, 90);
614         verifyBrightnessObserverCall(director, 0, 90, 60, 0, 60);
615         verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
616         verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
617     }
618 
619     @Test
620     public void testBrightnessObserver_LowPowerModeRemovesFlickerVotes() {
621         float[] refreshRates = {60.f, 90.f, 120.f};
622         DisplayModeDirector director =
623                 createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0);
624         SparseArray<Vote> votes = new SparseArray<>();
625         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
626         votesByDisplay.put(-1, votes); // Global Vote
627         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(0, 60));
628         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forRenderFrameRates(60, 90));
629         director.injectVotesByDisplay(votesByDisplay);
630 
631         director.getBrightnessObserver().onLowPowerModeEnabledLocked(true);
632 
633         assertNull(director.getVote(-1, Vote.PRIORITY_FLICKER_REFRESH_RATE));
634         assertNull(director.getVote(-1, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH));
635     }
636 
637     @Test
638     public void testVotingWithAlwaysRespectAppRequest() {
639         Display.Mode[] modes = new Display.Mode[3];
640         modes[0] = new Display.Mode(
641                 /*modeId=*/50, /*width=*/1000, /*height=*/1000, 50);
642         modes[1] = new Display.Mode(
643                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
644         modes[2] = new Display.Mode(
645                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
646 
647         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
648 
649         SparseArray<Vote> votes = new SparseArray<>();
650         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
651         votesByDisplay.put(DISPLAY_ID, votes);
652         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(0, 60));
653         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
654                 Vote.forRenderFrameRates(60, 90));
655         Display.Mode appRequestedMode = modes[2];
656         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
657                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
658         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
659                 Vote.forRenderFrameRates(60, 60));
660         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
661         director.injectVotesByDisplay(votesByDisplay);
662 
663         assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
664         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
665 
666         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
667         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
668         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
669         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
670         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
671 
672         director.setShouldAlwaysRespectAppRequestedMode(true);
673         assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
674         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
675         assertThat(desiredSpecs.primary.physical.min).isAtMost(50);
676         assertThat(desiredSpecs.primary.physical.max).isAtLeast(90);
677         assertThat(desiredSpecs.primary.render.min).isAtMost(50);
678         assertThat(desiredSpecs.primary.render.max).isAtLeast(90);
679         assertThat(desiredSpecs.baseModeId).isEqualTo(90);
680 
681         director.setShouldAlwaysRespectAppRequestedMode(false);
682         assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
683 
684         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
685         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
686         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
687         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
688         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
689         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
690     }
691 
692     @Test
693     public void testVotingWithSwitchingTypeNone() {
694         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
695         SparseArray<Vote> votes = new SparseArray<>();
696         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
697         votesByDisplay.put(DISPLAY_ID, votes);
698         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
699                 Vote.forRenderFrameRates(30, 90));
700         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
701 
702         director.injectVotesByDisplay(votesByDisplay);
703         assertThat(director.getModeSwitchingType())
704                 .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
705         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
706 
707         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
708         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
709         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
710         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
711         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
712         assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
713         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
714         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
715         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
716 
717         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
718         assertThat(director.getModeSwitchingType())
719                 .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
720 
721         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
722         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
723         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
724         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
725         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
726         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
727         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
728         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
729         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
730         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
731     }
732 
733     @Test
734     public void testVotingWithSwitchingTypeRenderFrameRateOnly() {
735         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
736         SparseArray<Vote> votes = new SparseArray<>();
737         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
738         votesByDisplay.put(DISPLAY_ID, votes);
739         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
740                 Vote.forRenderFrameRates(30, 90));
741         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
742 
743         director.injectVotesByDisplay(votesByDisplay);
744         assertThat(director.getModeSwitchingType())
745                 .isNotEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
746         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
747 
748         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
749         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
750         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
751         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
752         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
753         assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
754         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
755         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
756         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
757 
758         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
759         assertThat(director.getModeSwitchingType())
760                 .isEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
761 
762         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
763         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
764         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
765         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
766         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
767         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
768         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
769         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
770         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
771 
772         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
773     }
774 
775     @Test
776     public void testVotingWithSwitchingTypeRenderFrameRateOnlyRenderRateIsNotPhysicalRefreshRate() {
777         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
778         SparseArray<Vote> votes = new SparseArray<>();
779         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
780         votesByDisplay.put(DISPLAY_ID, votes);
781         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
782                 Vote.forRenderFrameRates(30, 90));
783         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
784 
785         director.injectVotesByDisplay(votesByDisplay);
786         assertThat(director.getModeSwitchingType())
787                 .isNotEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
788         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
789 
790         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
791         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
792         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
793         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
794         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
795         assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
796         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
797         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
798         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
799 
800         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
801         assertThat(director.getModeSwitchingType())
802                 .isEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
803 
804         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
805         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
806         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
807         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
808         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
809         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
810         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
811         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
812         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
813 
814         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
815     }
816 
817     @Test
818     public void testVotingWithSwitchingTypeWithinGroups() {
819         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
820 
821         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
822         assertThat(director.getModeSwitchingType())
823                 .isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
824         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
825         assertThat(desiredSpecs.allowGroupSwitching).isFalse();
826     }
827 
828     @Test
829     public void testVotingWithSwitchingTypeWithinAndAcrossGroups() {
830         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
831 
832         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
833         assertThat(director.getModeSwitchingType())
834                 .isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
835         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
836         assertThat(desiredSpecs.allowGroupSwitching).isTrue();
837     }
838 
839     @Test
840     public void testDefaultDisplayModeIsSelectedIfAvailable() {
841         final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
842         final int defaultModeId = 3;
843         DisplayModeDirector director = createDirectorFromRefreshRateArray(
844                 refreshRates, /*baseModeId=*/0, refreshRates[defaultModeId]);
845 
846         DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
847         assertThat(specs.baseModeId).isEqualTo(defaultModeId);
848     }
849 
850     @Test
851     public void testStaleAppRequestSize() {
852         DisplayModeDirector director =
853                 new DisplayModeDirector(mContext, mHandler, mInjector);
854         Display.Mode[] modes = new Display.Mode[] {
855                 new Display.Mode(1, 1280, 720, 60),
856         };
857         Display.Mode defaultMode = modes[0];
858 
859         // Inject supported modes
860         SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
861         supportedModesByDisplay.put(DISPLAY_ID, modes);
862         director.injectSupportedModesByDisplay(supportedModesByDisplay);
863 
864         // Inject default mode
865         SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
866         defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
867         director.injectDefaultModeByDisplay(defaultModesByDisplay);
868 
869         // Inject votes
870         SparseArray<Vote> votes = new SparseArray<>();
871         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(1920, 1080));
872         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
873                 Vote.forBaseModeRefreshRate(60));
874         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
875         votesByDisplay.put(DISPLAY_ID, votes);
876         director.injectVotesByDisplay(votesByDisplay);
877 
878         director.setShouldAlwaysRespectAppRequestedMode(true);
879 
880         // We should return the only available mode
881         DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
882         assertThat(specs.baseModeId).isEqualTo(defaultMode.getModeId());
883     }
884 
885     @Test
886     public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
887         DisplayModeDirector director =
888                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
889         SensorManager sensorManager = createMockSensorManager(createLightSensor());
890 
891         final int initialRefreshRate = 60;
892         mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
893         director.start(sensorManager);
894         assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
895                 .isEqualTo(initialRefreshRate);
896 
897         final int updatedRefreshRate = 90;
898         mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
899         // Need to wait for the property change to propagate to the main thread.
900         waitForIdleSync();
901         assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
902                 .isEqualTo(updatedRefreshRate);
903     }
904 
905     @Test
906     public void testBrightnessObserverThresholdsInZone() {
907         DisplayModeDirector director =
908                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
909         SensorManager sensorManager = createMockSensorManager(createLightSensor());
910 
911         final int[] initialDisplayThresholds = { 10 };
912         final int[] initialAmbientThresholds = { 20 };
913 
914         final FakeDeviceConfig config = mInjector.getDeviceConfig();
915         config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
916         config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
917         director.start(sensorManager);
918 
919         float[] expectedDisplayThresholds = { BrightnessSynchronizer.brightnessIntToFloat(10) };
920         float[] expectedAmbientThresholds = { 20 };
921         assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
922                 .isEqualTo(expectedDisplayThresholds);
923         assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
924                 .isEqualTo(expectedAmbientThresholds);
925 
926         final int[] updatedDisplayThresholds = { 9, 14 };
927         final int[] updatedAmbientThresholds = { -1, 19 };
928         config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
929         config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
930         // Need to wait for the property change to propagate to the main thread.
931         waitForIdleSync();
932 
933         expectedDisplayThresholds = new float[]{ BrightnessSynchronizer.brightnessIntToFloat(9),
934                 BrightnessSynchronizer.brightnessIntToFloat(14) };
935         expectedAmbientThresholds = new float[]{ -1, 19 };
936         assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
937                 .isEqualTo(expectedDisplayThresholds);
938         assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
939                 .isEqualTo(expectedAmbientThresholds);
940     }
941 
942     @Test
943     public void setBrightness_doesNotLockFpsIfSmoothDisplayIsOff() {
944         DisplayModeDirector director =
945                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
946         setPeakRefreshRate(60); // set smooth display OFF
947         director.getSettingsObserver().setDefaultRefreshRate(90);
948         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
949 
950         final FakeDeviceConfig config = mInjector.getDeviceConfig();
951         config.setRefreshRateInLowZone(90);
952         config.setLowDisplayBrightnessThresholds(new int[] { 10 });
953         config.setLowAmbientBrightnessThresholds(new int[] { 20 });
954 
955         director.start(createMockSensorManager(createLightSensor()));
956 
957         ArgumentCaptor<DisplayListener> displayListenerCaptor =
958                 ArgumentCaptor.forClass(DisplayListener.class);
959         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
960                 any(Handler.class),
961                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
962                         | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
963         DisplayListener displayListener = displayListenerCaptor.getValue();
964 
965         setBrightness(10, 10, displayListener);
966 
967         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
968         assertThat(vote).isNull();
969         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
970         assertThat(vote).isNull();
971     }
972 
973     @Test
974     public void setBrightness_locksFpsIfSmoothDisplayIsOn() {
975         DisplayModeDirector director =
976                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
977         setPeakRefreshRate(90); // set smooth display ON
978         director.getSettingsObserver().setDefaultRefreshRate(90);
979         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
980 
981         final FakeDeviceConfig config = mInjector.getDeviceConfig();
982         config.setRefreshRateInLowZone(90);
983         config.setLowDisplayBrightnessThresholds(new int[] { 10 });
984         config.setLowAmbientBrightnessThresholds(new int[] { 20 });
985 
986         director.start(createMockSensorManager(createLightSensor()));
987 
988         ArgumentCaptor<DisplayListener> displayListenerCaptor =
989                 ArgumentCaptor.forClass(DisplayListener.class);
990         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
991                 any(Handler.class),
992                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
993                         | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
994         DisplayListener displayListener = displayListenerCaptor.getValue();
995 
996         setBrightness(10, 10, displayListener);
997 
998         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
999         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
1000         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1001         assertThat(vote).isNotNull();
1002         assertThat(vote.disableRefreshRateSwitching).isTrue();
1003     }
1004 
1005     @Test
1006     public void setBrightness_doesNotLockFpsIfSmoothDisplayIsOnAndLowPowerMode() {
1007         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 1);
1008         DisplayModeDirector director =
1009                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
1010         setPeakRefreshRate(90); // set smooth display ON
1011         director.getSettingsObserver().setDefaultRefreshRate(90);
1012         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1013 
1014         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1015         config.setRefreshRateInLowZone(90);
1016         config.setLowDisplayBrightnessThresholds(new int[] { 10 });
1017         config.setLowAmbientBrightnessThresholds(new int[] { 20 });
1018 
1019         director.start(createMockSensorManager(createLightSensor()));
1020 
1021         ArgumentCaptor<DisplayListener> displayListenerCaptor =
1022                 ArgumentCaptor.forClass(DisplayListener.class);
1023         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
1024                 any(Handler.class),
1025                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
1026                         | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
1027         DisplayListener displayListener = displayListenerCaptor.getValue();
1028 
1029         setBrightness(10, 10, displayListener);
1030 
1031         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1032         assertThat(vote).isNull();
1033         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1034         assertThat(vote).isNull();
1035     }
1036 
1037     @Test
1038     public void testLockFpsForLowZone() throws Exception {
1039         DisplayModeDirector director =
1040                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
1041         setPeakRefreshRate(90);
1042         director.getSettingsObserver().setDefaultRefreshRate(90);
1043         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1044 
1045         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1046         config.setRefreshRateInLowZone(90);
1047         config.setLowDisplayBrightnessThresholds(new int[] { 10 });
1048         config.setLowAmbientBrightnessThresholds(new int[] { 20 });
1049 
1050         Sensor lightSensor = createLightSensor();
1051         SensorManager sensorManager = createMockSensorManager(lightSensor);
1052 
1053         director.start(sensorManager);
1054 
1055         ArgumentCaptor<DisplayListener> displayListenerCaptor =
1056                   ArgumentCaptor.forClass(DisplayListener.class);
1057         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
1058                 any(Handler.class),
1059                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
1060                     | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
1061         DisplayListener displayListener = displayListenerCaptor.getValue();
1062 
1063         ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
1064                 ArgumentCaptor.forClass(SensorEventListener.class);
1065         Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
1066                 .registerListener(
1067                         sensorListenerCaptor.capture(),
1068                         eq(lightSensor),
1069                         anyInt(),
1070                         any(Handler.class));
1071         SensorEventListener sensorListener = sensorListenerCaptor.getValue();
1072 
1073         setBrightness(10, 10, displayListener);
1074         // Sensor reads 20 lux,
1075         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
1076 
1077         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1078         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
1079         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1080         assertThat(vote).isNotNull();
1081         assertThat(vote.disableRefreshRateSwitching).isTrue();
1082 
1083         // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
1084         // parameter to the necessary threshold
1085         setBrightness(10, 125, displayListener);
1086         // Sensor reads 1000 lux,
1087         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
1088 
1089         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1090         assertThat(vote).isNull();
1091         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1092         assertThat(vote).isNull();
1093     }
1094 
1095     @Test
1096     public void testLockFpsForHighZone() throws Exception {
1097         DisplayModeDirector director =
1098                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
1099         setPeakRefreshRate(90 /*fps*/);
1100         director.getSettingsObserver().setDefaultRefreshRate(90);
1101         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1102 
1103         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1104         config.setRefreshRateInHighZone(60);
1105         config.setHighDisplayBrightnessThresholds(new int[] { 255 });
1106         config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
1107 
1108         Sensor lightSensor = createLightSensor();
1109         SensorManager sensorManager = createMockSensorManager(lightSensor);
1110 
1111         director.start(sensorManager);
1112 
1113         ArgumentCaptor<DisplayListener> displayListenerCaptor =
1114                   ArgumentCaptor.forClass(DisplayListener.class);
1115         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
1116                 any(Handler.class),
1117                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
1118                     | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
1119         DisplayListener displayListener = displayListenerCaptor.getValue();
1120 
1121         ArgumentCaptor<SensorEventListener> listenerCaptor =
1122                 ArgumentCaptor.forClass(SensorEventListener.class);
1123         verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
1124                 .registerListener(
1125                         listenerCaptor.capture(),
1126                         eq(lightSensor),
1127                         anyInt(),
1128                         any(Handler.class));
1129         SensorEventListener sensorListener = listenerCaptor.getValue();
1130 
1131         setBrightness(100, 100, displayListener);
1132         // Sensor reads 2000 lux,
1133         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
1134 
1135         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1136         assertThat(vote).isNull();
1137         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1138         assertThat(vote).isNull();
1139 
1140         // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
1141         // parameter to the necessary threshold
1142         setBrightness(100, 255, displayListener);
1143         // Sensor reads 9000 lux,
1144         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
1145 
1146         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1147         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
1148         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1149         assertThat(vote).isNotNull();
1150         assertThat(vote.disableRefreshRateSwitching).isTrue();
1151     }
1152 
1153     @Test
1154     public void testLockFpsForHighZoneWithThermalCondition() throws Exception {
1155         // First, configure brightness zones or DMD won't register for sensor data.
1156         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1157         config.setHighDisplayBrightnessThresholds(new int[] { 200 });
1158         config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
1159 
1160         DisplayModeDirector director =
1161                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
1162         setPeakRefreshRate(120 /*fps*/);
1163         director.getSettingsObserver().setDefaultRefreshRate(120);
1164         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1165 
1166         // Set the thresholds for High Zone
1167         DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
1168         when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90);
1169         when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
1170         when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
1171         when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
1172         when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {});
1173         when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {});
1174 
1175         // Set the thermal condition for refresh rate range
1176         when(ddcMock.getHighBlockingZoneThermalMap()).thenReturn(
1177                 new SparseArray<RefreshRateRange>() {{
1178                     put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
1179                 }}
1180         );
1181         director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
1182 
1183         Sensor lightSensor = createLightSensor();
1184         SensorManager sensorManager = createMockSensorManager(lightSensor);
1185         director.start(sensorManager);
1186 
1187         // Get the display listener so that we can send it new brightness events
1188         ArgumentCaptor<DisplayListener> displayListenerCaptor =
1189                   ArgumentCaptor.forClass(DisplayListener.class);
1190         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
1191                 any(Handler.class),
1192                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
1193                     | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
1194         DisplayListener displayListener = displayListenerCaptor.getValue();
1195 
1196         // Get the sensor listener so that we can give it new light sensor events
1197         ArgumentCaptor<SensorEventListener> listenerCaptor =
1198                 ArgumentCaptor.forClass(SensorEventListener.class);
1199         verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
1200                 .registerListener(
1201                         listenerCaptor.capture(),
1202                         eq(lightSensor),
1203                         anyInt(),
1204                         any(Handler.class));
1205         SensorEventListener sensorListener = listenerCaptor.getValue();
1206 
1207         // Get the thermal listener so that we can give it new thermal conditions
1208         ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
1209                 ArgumentCaptor.forClass(IThermalEventListener.class);
1210         verify(mInjector, atLeastOnce()).registerThermalServiceListener(
1211                 thermalListenerCaptor.capture());
1212         List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
1213 
1214         setBrightness(100, 100, displayListener);
1215         // Sensor reads 2000 lux,
1216         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
1217 
1218         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1219         assertThat(vote).isNull();
1220         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1221         assertThat(vote).isNull();
1222 
1223         // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
1224         // parameter to the necessary threshold
1225         setBrightness(255, 255, displayListener);
1226         // Sensor reads 9000 lux,
1227         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
1228 
1229         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1230         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
1231         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1232         assertThat(vote).isNotNull();
1233         assertThat(vote.disableRefreshRateSwitching).isTrue();
1234 
1235         // Set critical and check new refresh rate
1236         Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
1237         for (var listener : thermalListeners) {
1238             listener.notifyThrottling(temp);
1239         }
1240 
1241         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1242         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
1243         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1244         assertThat(vote).isNotNull();
1245         assertThat(vote.disableRefreshRateSwitching).isTrue();
1246     }
1247 
1248     @Test
1249     public void testLockFpsForLowZoneWithThermalCondition() throws Exception {
1250         // First, configure brightness zones or DMD won't register for sensor data.
1251         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1252         config.setHighDisplayBrightnessThresholds(new int[] { 200 });
1253         config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
1254 
1255         DisplayModeDirector director =
1256                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
1257         setPeakRefreshRate(120 /*fps*/);
1258         director.getSettingsObserver().setDefaultRefreshRate(120);
1259         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1260 
1261         // Set the thresholds for Low Zone
1262         DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
1263         when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
1264         when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
1265         when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
1266         when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
1267         when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] { 10 });
1268         when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] { 10 });
1269 
1270         // Set the thermal condition for refresh rate range
1271         when(ddcMock.getLowBlockingZoneThermalMap()).thenReturn(
1272                 new SparseArray<RefreshRateRange>() {{
1273                     put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
1274                 }}
1275         );
1276         director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
1277 
1278         Sensor lightSensor = createLightSensor();
1279         SensorManager sensorManager = createMockSensorManager(lightSensor);
1280         director.start(sensorManager);
1281 
1282         // Get the display listener so that we can send it new brightness events
1283         ArgumentCaptor<DisplayListener> displayListenerCaptor =
1284                   ArgumentCaptor.forClass(DisplayListener.class);
1285         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
1286                 any(Handler.class),
1287                 eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
1288                     | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
1289         DisplayListener displayListener = displayListenerCaptor.getValue();
1290 
1291         // Get the sensor listener so that we can give it new light sensor events
1292         ArgumentCaptor<SensorEventListener> listenerCaptor =
1293                 ArgumentCaptor.forClass(SensorEventListener.class);
1294         verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
1295                 .registerListener(
1296                         listenerCaptor.capture(),
1297                         eq(lightSensor),
1298                         anyInt(),
1299                         any(Handler.class));
1300         SensorEventListener sensorListener = listenerCaptor.getValue();
1301 
1302         // Get the thermal listener so that we can give it new thermal conditions
1303         ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
1304                 ArgumentCaptor.forClass(IThermalEventListener.class);
1305         verify(mInjector, atLeastOnce()).registerThermalServiceListener(
1306                 thermalListenerCaptor.capture());
1307         List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
1308 
1309         setBrightness(100, 100, displayListener);
1310         // Sensor reads 2000 lux,
1311         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
1312 
1313         Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1314         assertThat(vote).isNull();
1315         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1316         assertThat(vote).isNull();
1317 
1318         // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
1319         // parameter to the necessary threshold
1320         setBrightness(5, 5, displayListener);
1321         // Sensor reads 9 lux,
1322         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9));
1323 
1324         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1325         assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
1326         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1327         assertThat(vote).isNotNull();
1328         assertThat(vote.disableRefreshRateSwitching).isTrue();
1329 
1330         // Set critical and check new refresh rate
1331         Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
1332         for (var listener : thermalListeners) {
1333             listener.notifyThrottling(temp);
1334         }
1335 
1336         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
1337         assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
1338         vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
1339         assertThat(vote).isNotNull();
1340         assertThat(vote.disableRefreshRateSwitching).isTrue();
1341     }
1342 
1343     @Test
1344     public void testSensorRegistration() {
1345         // First, configure brightness zones or DMD won't register for sensor data.
1346         final FakeDeviceConfig config = mInjector.getDeviceConfig();
1347         config.setRefreshRateInHighZone(60);
1348         config.setHighDisplayBrightnessThresholds(new int[] { 255 });
1349         config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
1350 
1351         DisplayModeDirector director =
1352                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
1353         setPeakRefreshRate(90 /*fps*/);
1354         director.getSettingsObserver().setDefaultRefreshRate(90);
1355         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1356 
1357         Sensor lightSensor = createLightSensor();
1358         SensorManager sensorManager = createMockSensorManager(lightSensor);
1359 
1360         director.start(sensorManager);
1361         ArgumentCaptor<SensorEventListener> listenerCaptor =
1362                 ArgumentCaptor.forClass(SensorEventListener.class);
1363         verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
1364                 .registerListener(
1365                         listenerCaptor.capture(),
1366                         eq(lightSensor),
1367                         anyInt(),
1368                         any(Handler.class));
1369 
1370         // Display state changed from On to Doze
1371         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
1372         verify(sensorManager)
1373                 .unregisterListener(listenerCaptor.capture());
1374 
1375         // Display state changed from Doze to On
1376         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
1377         verify(sensorManager, times(2))
1378                 .registerListener(
1379                         listenerCaptor.capture(),
1380                         eq(lightSensor),
1381                         anyInt(),
1382                         any(Handler.class));
1383 
1384     }
1385 
1386     @Test
1387     public void testUdfpsListenerGetsRegistered() {
1388         DisplayModeDirector director =
1389                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
1390         verify(mStatusBarMock, never()).setUdfpsRefreshRateCallback(any());
1391 
1392         director.onBootCompleted();
1393         verify(mStatusBarMock).setUdfpsRefreshRateCallback(eq(director.getUdpfsObserver()));
1394     }
1395 
1396     @Test
1397     public void testGbhmVotesFor60hz() throws Exception {
1398         DisplayModeDirector director =
1399                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
1400         director.start(createMockSensorManager());
1401         director.onBootCompleted();
1402         ArgumentCaptor<IUdfpsRefreshRateRequestCallback> captor =
1403                 ArgumentCaptor.forClass(IUdfpsRefreshRateRequestCallback.class);
1404         verify(mStatusBarMock).setUdfpsRefreshRateCallback(captor.capture());
1405 
1406         // Should be no vote initially
1407         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
1408         assertNull(vote);
1409     }
1410 
1411     @Test
1412     public void testAppRequestMinRefreshRate() {
1413         // Confirm that the app min request range doesn't include flicker or min refresh rate
1414         // settings but does include everything else.
1415         assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
1416                 >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
1417 
1418         Display.Mode[] modes = new Display.Mode[3];
1419         modes[0] = new Display.Mode(
1420                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
1421         modes[1] = new Display.Mode(
1422                 /*modeId=*/75, /*width=*/1000, /*height=*/1000, 75);
1423         modes[2] = new Display.Mode(
1424                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
1425 
1426         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
1427         SparseArray<Vote> votes = new SparseArray<>();
1428         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1429         votesByDisplay.put(DISPLAY_ID, votes);
1430         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
1431         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60));
1432         director.injectVotesByDisplay(votesByDisplay);
1433         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1434         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
1435         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
1436         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
1437         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1438 
1439         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1440                 Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
1441         director.injectVotesByDisplay(votesByDisplay);
1442         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1443         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1444         assertThat(desiredSpecs.primary.physical.max).isAtLeast(90f);
1445         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
1446         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1447 
1448         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
1449                 Vote.forRenderFrameRates(75, Float.POSITIVE_INFINITY));
1450         director.injectVotesByDisplay(votesByDisplay);
1451         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1452         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1453         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
1454         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
1455         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1456     }
1457 
1458     @Test
testAppRequestMaxRefreshRate()1459     public void testAppRequestMaxRefreshRate() {
1460         // Confirm that the app max request range doesn't include flicker or min refresh rate
1461         // settings but does include everything else.
1462         assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
1463                 >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
1464 
1465         Display.Mode[] modes = new Display.Mode[3];
1466         modes[0] = new Display.Mode(
1467                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
1468         modes[1] = new Display.Mode(
1469                 /*modeId=*/75, /*width=*/1000, /*height=*/1000, 75);
1470         modes[2] = new Display.Mode(
1471                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
1472 
1473         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
1474         SparseArray<Vote> votes = new SparseArray<>();
1475         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1476         votesByDisplay.put(DISPLAY_ID, votes);
1477         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
1478         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forPhysicalRefreshRates(60, 60));
1479         director.injectVotesByDisplay(votesByDisplay);
1480         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1481         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
1482         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
1483         assertThat(desiredSpecs.primary.render.min).isZero();
1484         assertThat(desiredSpecs.primary.render.max).isAtMost(60);
1485         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
1486         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1487         assertThat(desiredSpecs.appRequest.render.min).isZero();
1488         assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f);
1489 
1490         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1491                 Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
1492         director.injectVotesByDisplay(votesByDisplay);
1493         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1494         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1495         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
1496         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
1497         assertThat(desiredSpecs.primary.render.max).isAtLeast(90f);
1498         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
1499         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1500         assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f);
1501         assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f);
1502 
1503         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
1504                 Vote.forRenderFrameRates(0, 75));
1505         director.injectVotesByDisplay(votesByDisplay);
1506         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1507         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
1508         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75);
1509         assertThat(desiredSpecs.primary.render.min).isZero();
1510         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(75);
1511         assertThat(desiredSpecs.appRequest.physical.min).isZero();
1512         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
1513         assertThat(desiredSpecs.appRequest.render.min).isZero();
1514         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(75);
1515     }
1516 
1517     @Test
testAppRequestObserver_modeId()1518     public void testAppRequestObserver_modeId() {
1519         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
1520         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
1521 
1522         Vote appRequestRefreshRate =
1523                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1524         assertNotNull(appRequestRefreshRate);
1525         assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
1526         assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
1527         assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
1528         assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
1529         assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
1530         assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
1531                 .isWithin(FLOAT_TOLERANCE).of(60);
1532         assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
1533         assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
1534 
1535         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1536         assertNotNull(appRequestSize);
1537         assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
1538         assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
1539         assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
1540         assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
1541         assertThat(appRequestSize.disableRefreshRateSwitching).isFalse();
1542         assertThat(appRequestSize.appRequestBaseModeRefreshRate).isZero();
1543         assertThat(appRequestSize.height).isEqualTo(1000);
1544         assertThat(appRequestSize.width).isEqualTo(1000);
1545 
1546         Vote appRequestRefreshRateRange =
1547                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1548         assertNull(appRequestRefreshRateRange);
1549 
1550         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0);
1551 
1552         appRequestRefreshRate =
1553                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1554         assertNotNull(appRequestRefreshRate);
1555         assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
1556         assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
1557         assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
1558         assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
1559         assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
1560         assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
1561                 .isWithin(FLOAT_TOLERANCE).of(90);
1562         assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
1563         assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
1564 
1565         appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1566         assertNotNull(appRequestSize);
1567         assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
1568         assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
1569         assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
1570         assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
1571         assertThat(appRequestSize.height).isEqualTo(1000);
1572         assertThat(appRequestSize.width).isEqualTo(1000);
1573 
1574         appRequestRefreshRateRange =
1575                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1576         assertNull(appRequestRefreshRateRange);
1577     }
1578 
1579     @Test
testAppRequestObserver_minRefreshRate()1580     public void testAppRequestObserver_minRefreshRate() {
1581         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
1582         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0);
1583         Vote appRequestRefreshRate =
1584                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1585         assertNull(appRequestRefreshRate);
1586 
1587         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1588         assertNull(appRequestSize);
1589 
1590         Vote appRequestRefreshRateRange =
1591                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1592         assertNotNull(appRequestRefreshRateRange);
1593         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
1594         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
1595                 .isPositiveInfinity();
1596         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
1597                 .isWithin(FLOAT_TOLERANCE).of(60);
1598         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
1599         assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
1600         assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
1601 
1602         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 0);
1603         appRequestRefreshRate =
1604                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1605         assertNull(appRequestRefreshRate);
1606 
1607         appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1608         assertNull(appRequestSize);
1609 
1610         appRequestRefreshRateRange =
1611                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1612         assertNotNull(appRequestRefreshRateRange);
1613         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
1614         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
1615                 .isPositiveInfinity();
1616 
1617         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
1618                 .isWithin(FLOAT_TOLERANCE).of(90);
1619         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
1620         assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
1621         assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
1622     }
1623 
1624     @Test
testAppRequestObserver_maxRefreshRate()1625     public void testAppRequestObserver_maxRefreshRate() {
1626         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
1627         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90);
1628         Vote appRequestRefreshRate =
1629                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1630         assertNull(appRequestRefreshRate);
1631 
1632         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1633         assertNull(appRequestSize);
1634 
1635         Vote appRequestRefreshRateRange =
1636                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1637         assertNotNull(appRequestRefreshRateRange);
1638         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
1639         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
1640                 .isPositiveInfinity();
1641 
1642         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
1643         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
1644                 .isWithin(FLOAT_TOLERANCE).of(90);
1645         assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
1646         assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
1647 
1648         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 60);
1649         appRequestRefreshRate =
1650                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1651         assertNull(appRequestRefreshRate);
1652 
1653         appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1654         assertNull(appRequestSize);
1655 
1656         appRequestRefreshRateRange =
1657                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1658         assertNotNull(appRequestRefreshRateRange);
1659         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
1660         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
1661                 .isPositiveInfinity();
1662 
1663         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
1664         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
1665                 .isWithin(FLOAT_TOLERANCE).of(60);
1666         assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
1667         assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
1668     }
1669 
1670     @Test
testAppRequestObserver_invalidRefreshRateRange()1671     public void testAppRequestObserver_invalidRefreshRateRange() {
1672         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
1673         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 60);
1674         Vote appRequestRefreshRate =
1675                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1676         assertNull(appRequestRefreshRate);
1677 
1678         Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1679         assertNull(appRequestSize);
1680 
1681         Vote appRequestRefreshRateRange =
1682                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1683         assertNull(appRequestRefreshRateRange);
1684     }
1685 
1686     @Test
testAppRequestObserver_modeIdAndRefreshRateRange()1687     public void testAppRequestObserver_modeIdAndRefreshRateRange() {
1688         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
1689         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
1690 
1691         Vote appRequestRefreshRate =
1692                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
1693         assertNotNull(appRequestRefreshRate);
1694         assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
1695         assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
1696         assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
1697         assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
1698         assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
1699         assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
1700                 .isWithin(FLOAT_TOLERANCE).of(60);
1701         assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
1702         assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
1703 
1704         Vote appRequestSize =
1705                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
1706         assertNotNull(appRequestSize);
1707         assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
1708         assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
1709         assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
1710         assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
1711         assertThat(appRequestSize.height).isEqualTo(1000);
1712         assertThat(appRequestSize.width).isEqualTo(1000);
1713 
1714         Vote appRequestRefreshRateRange =
1715                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
1716         assertNotNull(appRequestRefreshRateRange);
1717         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
1718         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
1719                 .isPositiveInfinity();
1720         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
1721                 .isWithin(FLOAT_TOLERANCE).of(90);
1722         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
1723                 .isWithin(FLOAT_TOLERANCE).of(90);
1724         assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
1725         assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
1726     }
1727 
1728     @Test
testAppRequestsIsTheDefaultMode()1729     public void testAppRequestsIsTheDefaultMode() {
1730         Display.Mode[] modes = new Display.Mode[2];
1731         modes[0] = new Display.Mode(
1732                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
1733         modes[1] = new Display.Mode(
1734                 /*modeId=*/2, /*width=*/1000, /*height=*/1000, 90);
1735 
1736         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
1737         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1738         assertThat(desiredSpecs.baseModeId).isEqualTo(1);
1739         assertThat(desiredSpecs.primary.physical.min).isAtMost(60);
1740         assertThat(desiredSpecs.primary.physical.max).isAtLeast(90);
1741 
1742         SparseArray<Vote> votes = new SparseArray<>();
1743         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1744         votesByDisplay.put(DISPLAY_ID, votes);
1745         Display.Mode appRequestedMode = modes[1];
1746         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1747                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
1748         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
1749                 appRequestedMode.getPhysicalHeight()));
1750         director.injectVotesByDisplay(votesByDisplay);
1751         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1752         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
1753         assertThat(desiredSpecs.primary.physical.min).isAtMost(60);
1754         assertThat(desiredSpecs.primary.physical.max).isAtLeast(90);
1755     }
1756 
1757     @Test
testDisableRefreshRateSwitchingVote()1758     public void testDisableRefreshRateSwitchingVote() {
1759         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
1760         SparseArray<Vote> votes = new SparseArray<>();
1761         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1762         votesByDisplay.put(DISPLAY_ID, votes);
1763         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1764                 Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
1765         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
1766         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
1767         director.injectVotesByDisplay(votesByDisplay);
1768         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1769         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50);
1770         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(50);
1771         assertThat(desiredSpecs.baseModeId).isEqualTo(50);
1772 
1773         votes.clear();
1774         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE,
1775                 Vote.forPhysicalRefreshRates(70, Float.POSITIVE_INFINITY));
1776         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1777                 Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
1778         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
1779         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
1780         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1781         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80);
1782         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80);
1783         assertThat(desiredSpecs.baseModeId).isEqualTo(80);
1784 
1785         votes.clear();
1786         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE,
1787                 Vote.forPhysicalRefreshRates(90, Float.POSITIVE_INFINITY));
1788         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1789                 Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
1790         votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
1791         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
1792         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1793         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1794         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
1795         assertThat(desiredSpecs.baseModeId).isEqualTo(90);
1796     }
1797 
1798     @Test
1799     @Parameters({
1800             "true",
1801             "false"
1802     })
testBaseModeIdInPrimaryRange(boolean supportsFrameRateOverride)1803     public void testBaseModeIdInPrimaryRange(boolean supportsFrameRateOverride) {
1804         when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
1805         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
1806         SparseArray<Vote> votes = new SparseArray<>();
1807         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1808         votesByDisplay.put(DISPLAY_ID, votes);
1809         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1810                 Vote.forBaseModeRefreshRate(70));
1811         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
1812         director.injectVotesByDisplay(votesByDisplay);
1813         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1814         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
1815         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
1816         if (supportsFrameRateOverride) {
1817             assertThat(desiredSpecs.baseModeId).isEqualTo(70);
1818         } else {
1819             assertThat(desiredSpecs.baseModeId).isEqualTo(50);
1820 
1821         }
1822         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
1823         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
1824 
1825         votes.clear();
1826         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1827                 Vote.forBaseModeRefreshRate(55));
1828         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
1829         director.injectVotesByDisplay(votesByDisplay);
1830         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1831         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
1832         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
1833         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
1834         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
1835         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
1836 
1837         votes.clear();
1838         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
1839                 Vote.forRenderFrameRates(0, 52));
1840         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1841                 Vote.forBaseModeRefreshRate(55));
1842         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
1843         director.injectVotesByDisplay(votesByDisplay);
1844         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1845         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
1846         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
1847         if (supportsFrameRateOverride) {
1848             assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(52);
1849         } else {
1850             assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
1851         }
1852         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
1853         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
1854 
1855         votes.clear();
1856         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
1857                 Vote.forRenderFrameRates(0, 58));
1858         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1859                 Vote.forBaseModeRefreshRate(55));
1860         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
1861         director.injectVotesByDisplay(votesByDisplay);
1862         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1863         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
1864         assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
1865         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
1866         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(58);
1867         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
1868     }
1869 
1870     @Test
testStaleAppVote()1871     public void testStaleAppVote() {
1872         Display.Mode[] modes = new Display.Mode[4];
1873         modes[0] = new Display.Mode(
1874                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
1875         modes[1] = new Display.Mode(
1876                 /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60);
1877         modes[2] = new Display.Mode(
1878                 /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90);
1879         modes[3] = new Display.Mode(
1880                 /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90);
1881 
1882         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]);
1883         SparseArray<Vote> votes = new SparseArray<>();
1884         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1885         votesByDisplay.put(DISPLAY_ID, votes);
1886         Display.Mode appRequestedMode = modes[3];
1887         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1888                 Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
1889         votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
1890                 appRequestedMode.getPhysicalHeight()));
1891         director.injectVotesByDisplay(votesByDisplay);
1892         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1893         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
1894 
1895         // Change mode Id's to simulate that a hotplug has occurred.
1896         Display.Mode[] newModes = new Display.Mode[4];
1897         newModes[0] = new Display.Mode(
1898                 /*modeId=*/5, /*width=*/1000, /*height=*/1000, 60);
1899         newModes[1] = new Display.Mode(
1900                 /*modeId=*/6, /*width=*/2000, /*height=*/2000, 60);
1901         newModes[2] = new Display.Mode(
1902                 /*modeId=*/7, /*width=*/1000, /*height=*/1000, 90);
1903         newModes[3] = new Display.Mode(
1904                 /*modeId=*/8, /*width=*/2000, /*height=*/2000, 90);
1905 
1906         SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
1907         supportedModesByDisplay.put(DISPLAY_ID, newModes);
1908         director.injectSupportedModesByDisplay(supportedModesByDisplay);
1909         SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
1910         defaultModesByDisplay.put(DISPLAY_ID, newModes[0]);
1911         director.injectDefaultModeByDisplay(defaultModesByDisplay);
1912         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1913         assertThat(desiredSpecs.baseModeId).isEqualTo(8);
1914     }
1915 
1916     @Test
1917     @Parameters({
1918             "true",
1919             "false"
1920     })
testRefreshRateIsSubsetOfFrameRate(boolean supportsFrameRateOverride)1921     public void testRefreshRateIsSubsetOfFrameRate(boolean supportsFrameRateOverride) {
1922         when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
1923         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
1924         SparseArray<Vote> votes = new SparseArray<>();
1925         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1926         votesByDisplay.put(DISPLAY_ID, votes);
1927 
1928 
1929         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120));
1930         director.injectVotesByDisplay(votesByDisplay);
1931         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1932         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1933         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
1934         assertThat(desiredSpecs.appRequest.render.min).isZero();
1935         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
1936 
1937         votes.clear();
1938         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120));
1939         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
1940                 Vote.forRenderFrameRates(0, 60));
1941         director.injectVotesByDisplay(votesByDisplay);
1942         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1943         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1944         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
1945         assertThat(desiredSpecs.appRequest.render.min).isZero();
1946         if (supportsFrameRateOverride) {
1947             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
1948         } else {
1949             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
1950         }
1951 
1952         votes.clear();
1953         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120));
1954         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
1955                 Vote.forRenderFrameRates(60, 60));
1956         director.injectVotesByDisplay(votesByDisplay);
1957         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1958         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1959         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
1960         if (supportsFrameRateOverride) {
1961             assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(60);
1962             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
1963         } else {
1964             assertThat(desiredSpecs.appRequest.render.min).isZero();
1965             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
1966         }
1967 
1968         votes.clear();
1969         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(90, 120));
1970         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
1971                 Vote.forRenderFrameRates(140, 140));
1972         director.injectVotesByDisplay(votesByDisplay);
1973         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1974         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
1975         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
1976         assertThat(desiredSpecs.appRequest.render.min).isZero();
1977         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
1978     }
1979 
1980     @Test
testRenderFrameRateIsAchievableByPhysicalRefreshRate()1981     public void testRenderFrameRateIsAchievableByPhysicalRefreshRate() {
1982         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
1983         SparseArray<Vote> votes = new SparseArray<>();
1984         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
1985         votesByDisplay.put(DISPLAY_ID, votes);
1986 
1987 
1988         votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120));
1989         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
1990         director.injectVotesByDisplay(votesByDisplay);
1991         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
1992         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120);
1993         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
1994         assertThat(desiredSpecs.appRequest.render.min).isZero();
1995         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
1996     }
1997 
1998     @Test
1999     @Parameters({
2000             "true",
2001             "false"
2002     })
testRenderFrameRateIncludesPhysicalRefreshRate(boolean supportsFrameRateOverride)2003     public void testRenderFrameRateIncludesPhysicalRefreshRate(boolean supportsFrameRateOverride) {
2004         when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
2005         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
2006         SparseArray<Vote> votes = new SparseArray<>();
2007         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
2008         votesByDisplay.put(DISPLAY_ID, votes);
2009 
2010         votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
2011         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
2012                 Vote.forRenderFrameRates(0, 30));
2013         director.injectVotesByDisplay(votesByDisplay);
2014         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
2015         assertThat(desiredSpecs.appRequest.physical.min).isZero();
2016         assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
2017         assertThat(desiredSpecs.appRequest.render.min).isZero();
2018         if (supportsFrameRateOverride) {
2019             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
2020         } else {
2021             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
2022         }
2023     }
2024 
2025     @Test
testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate()2026     public void testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate() {
2027         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
2028         SparseArray<Vote> votes = new SparseArray<>();
2029         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
2030         votesByDisplay.put(DISPLAY_ID, votes);
2031         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
2032                 Vote.forRenderFrameRates(120, 120));
2033         votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
2034                 Vote.forBaseModeRefreshRate(90));
2035         votes.put(Vote.PRIORITY_PROXIMITY, Vote.forPhysicalRefreshRates(60, 120));
2036         director.injectVotesByDisplay(votesByDisplay);
2037         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
2038         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
2039         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
2040         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
2041         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(120);
2042         assertThat(desiredSpecs.baseModeId).isEqualTo(90);
2043     }
2044 
2045     @Test
testRenderFrameRateIsAchievableWhenSwitchingTypeNone()2046     public void testRenderFrameRateIsAchievableWhenSwitchingTypeNone() {
2047         Display.Mode[] modes = new Display.Mode[2];
2048         modes[0] = new Display.Mode(
2049                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
2050         modes[1] = new Display.Mode(
2051                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
2052 
2053         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
2054         director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
2055 
2056         SparseArray<Vote> votes = new SparseArray<>();
2057         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
2058         votesByDisplay.put(DISPLAY_ID, votes);
2059         votes.put(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
2060                 Vote.forRenderFrameRates(0, 60));
2061         director.injectVotesByDisplay(votesByDisplay);
2062         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
2063         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
2064         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
2065         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
2066         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
2067         assertThat(desiredSpecs.baseModeId).isEqualTo(90);
2068     }
2069 
2070     @Test
testProximitySensorVoting()2071     public void testProximitySensorVoting() {
2072         DisplayModeDirector director =
2073                 createDirectorFromRefreshRateArray(new float[]{60.f, 90.f}, 0);
2074         director.start(createMockSensorManager());
2075 
2076         ArgumentCaptor<ProximityActiveListener> ProximityCaptor =
2077                 ArgumentCaptor.forClass(ProximityActiveListener.class);
2078         verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class),
2079                 ProximityCaptor.capture());
2080         ProximityActiveListener proximityListener = ProximityCaptor.getValue();
2081 
2082         ArgumentCaptor<DisplayListener> DisplayCaptor =
2083                 ArgumentCaptor.forClass(DisplayListener.class);
2084         verify(mInjector, times(2)).registerDisplayListener(DisplayCaptor.capture(),
2085                 any(Handler.class),
2086                 eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED
2087                         | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
2088                         | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2089         DisplayListener displayListener = DisplayCaptor.getAllValues().get(0);
2090 
2091         // Verify that there is no proximity vote initially
2092         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2093         assertNull(vote);
2094 
2095         when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null),
2096                   eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60));
2097 
2098         when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
2099 
2100         // Set the proximity to active and verify that we added a vote.
2101         proximityListener.onProximityActive(true);
2102         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2103         assertVoteForPhysicalRefreshRate(vote, 60.f);
2104 
2105         // Set the display state to doze and verify that the vote is gone
2106         when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
2107         displayListener.onDisplayAdded(DISPLAY_ID);
2108         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2109         assertNull(vote);
2110 
2111         // Set the display state to on and verify that we added the vote back.
2112         when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
2113         displayListener.onDisplayChanged(DISPLAY_ID);
2114         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2115         assertVoteForPhysicalRefreshRate(vote, 60.f);
2116 
2117         // Set the display state to doze and verify that the vote is gone
2118         when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
2119         displayListener.onDisplayAdded(DISPLAY_ID);
2120         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2121         assertNull(vote);
2122 
2123         // Remove the display to cause the doze state to be removed
2124         displayListener.onDisplayRemoved(DISPLAY_ID);
2125         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2126         assertVoteForPhysicalRefreshRate(vote, 60.f);
2127 
2128         // Turn prox off and verify vote is gone.
2129         proximityListener.onProximityActive(false);
2130         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
2131         assertNull(vote);
2132     }
2133 
2134     @Test
testHbmVoting_forHdr()2135     public void testHbmVoting_forHdr() {
2136         DisplayModeDirector director =
2137                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2138         final int hbmRefreshRate = 72;
2139 
2140         // Specify limitation before starting DisplayModeDirector to avoid waiting on property
2141         // propagation
2142         mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hbmRefreshRate);
2143 
2144         director.start(createMockSensorManager());
2145 
2146         ArgumentCaptor<DisplayListener> captor =
2147                   ArgumentCaptor.forClass(DisplayListener.class);
2148         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2149                   eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2150                   | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2151         DisplayListener listener = captor.getValue();
2152 
2153         // Specify Limitation
2154         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
2155                 List.of(new RefreshRateLimitation(
2156                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
2157                         60.f, 60.f)));
2158 
2159         // Verify that there is no HBM vote initially
2160         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2161         assertNull(vote);
2162 
2163         // Turn on HBM, with brightness in the HBM range
2164         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2165                 new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
2166                     BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
2167                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2168         listener.onDisplayChanged(DISPLAY_ID);
2169         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2170         assertVoteForPhysicalRefreshRate(vote, hbmRefreshRate);
2171 
2172         // Turn on HBM, with brightness below the HBM range
2173         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2174                 new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
2175                     BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
2176                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2177         listener.onDisplayChanged(DISPLAY_ID);
2178         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2179         assertNull(vote);
2180 
2181         // Turn off HBM
2182         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2183                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2184                     TRANSITION_POINT,
2185                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2186         listener.onDisplayChanged(DISPLAY_ID);
2187         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2188         assertNull(vote);
2189 
2190         // Turn on HBM, with brightness in the HBM range
2191         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2192                 new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
2193                     BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
2194                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2195         listener.onDisplayChanged(DISPLAY_ID);
2196         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2197         assertVoteForPhysicalRefreshRate(vote, hbmRefreshRate);
2198 
2199         // Turn off HBM
2200         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2201                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2202                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2203         listener.onDisplayChanged(DISPLAY_ID);
2204         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2205         assertNull(vote);
2206 
2207         // Turn on HBM, with brightness below the HBM range
2208         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2209                 new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
2210                     BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
2211                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2212         listener.onDisplayChanged(DISPLAY_ID);
2213         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2214         assertNull(vote);
2215 
2216         // Turn off HBM
2217         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2218                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2219                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2220         listener.onDisplayChanged(DISPLAY_ID);
2221         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2222         assertNull(vote);
2223     }
2224 
2225     @Test
testHbmObserverGetsUpdatedRefreshRateInHbmSunlight()2226     public void testHbmObserverGetsUpdatedRefreshRateInHbmSunlight() {
2227         DisplayModeDirector director =
2228                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
2229 
2230         final int initialRefreshRate = 60;
2231         mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(initialRefreshRate);
2232         director.start(createMockSensorManager());
2233         assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
2234                 .isEqualTo(initialRefreshRate);
2235 
2236         final int updatedRefreshRate = 90;
2237         mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
2238         // Need to wait for the property change to propagate to the main thread.
2239         waitForIdleSync();
2240         assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
2241                 .isEqualTo(updatedRefreshRate);
2242     }
2243 
2244     @Test
testHbmObserverGetsUpdatedRefreshRateInHbmHdr()2245     public void testHbmObserverGetsUpdatedRefreshRateInHbmHdr() {
2246         DisplayModeDirector director =
2247                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
2248 
2249         final int initialRefreshRate = 60;
2250         mInjector.getDeviceConfig().setRefreshRateInHbmHdr(initialRefreshRate);
2251         director.start(createMockSensorManager());
2252         assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
2253                 .isEqualTo(initialRefreshRate);
2254 
2255         final int updatedRefreshRate = 90;
2256         mInjector.getDeviceConfig().setRefreshRateInHbmHdr(updatedRefreshRate);
2257         // Need to wait for the property change to propagate to the main thread.
2258         waitForIdleSync();
2259         assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
2260                 .isEqualTo(updatedRefreshRate);
2261     }
2262 
2263     @Test
testHbmVoting_forSunlight()2264     public void testHbmVoting_forSunlight() {
2265         DisplayModeDirector director =
2266                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2267         director.start(createMockSensorManager());
2268 
2269         ArgumentCaptor<DisplayListener> captor =
2270                   ArgumentCaptor.forClass(DisplayListener.class);
2271         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2272                   eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2273                   | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2274         DisplayListener listener = captor.getValue();
2275 
2276         final int initialRefreshRate = 60;
2277         // Specify Limitation
2278         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
2279                 List.of(new RefreshRateLimitation(
2280                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
2281                         initialRefreshRate, initialRefreshRate)));
2282 
2283         // Verify that there is no HBM vote initially
2284         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2285         assertNull(vote);
2286 
2287         // Turn on HBM
2288         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2289                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2290                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2291         listener.onDisplayChanged(DISPLAY_ID);
2292         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2293         assertVoteForPhysicalRefreshRate(vote, initialRefreshRate);
2294 
2295         // Change refresh rate vote value through DeviceConfig, ensure it takes precedence
2296         final int updatedRefreshRate = 90;
2297         mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
2298         // Need to wait for the property change to propagate to the main thread.
2299         waitForIdleSync();
2300         assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
2301                 .isEqualTo(updatedRefreshRate);
2302         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2303         assertVoteForPhysicalRefreshRate(vote, updatedRefreshRate);
2304 
2305         // Turn off HBM
2306         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2307                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2308                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2309         listener.onDisplayChanged(DISPLAY_ID);
2310         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2311         assertNull(vote);
2312 
2313         // Turn HBM on again and ensure the updated vote value stuck
2314         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2315                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2316                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2317         listener.onDisplayChanged(DISPLAY_ID);
2318         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2319         assertVoteForPhysicalRefreshRate(vote, updatedRefreshRate);
2320 
2321         // Reset DeviceConfig refresh rate, ensure vote falls back to the initial value
2322         mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0);
2323         // Need to wait for the property change to propagate to the main thread.
2324         waitForIdleSync();
2325         assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0);
2326         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2327         assertVoteForPhysicalRefreshRate(vote, initialRefreshRate);
2328 
2329         // Turn off HBM
2330         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2331                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2332                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2333         listener.onDisplayChanged(DISPLAY_ID);
2334         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2335         assertNull(vote);
2336     }
2337 
2338     @Test
testHbmVoting_forSunlight_NoLimitation()2339     public void testHbmVoting_forSunlight_NoLimitation() {
2340         DisplayModeDirector director =
2341                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2342         director.start(createMockSensorManager());
2343 
2344         ArgumentCaptor<DisplayListener> captor =
2345                   ArgumentCaptor.forClass(DisplayListener.class);
2346         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2347                   eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2348                   | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2349         DisplayListener listener = captor.getValue();
2350 
2351         // Specify Limitation for different display
2352         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID + 1)).thenReturn(
2353                 List.of(new RefreshRateLimitation(
2354                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
2355                         60.f, 60.f)));
2356 
2357         // Verify that there is no HBM vote initially
2358         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2359         assertNull(vote);
2360 
2361         // Turn on HBM
2362         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2363                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2364                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2365         listener.onDisplayChanged(DISPLAY_ID);
2366         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2367         assertNull(vote);
2368 
2369         // Turn off HBM
2370         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2371                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2372                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2373         listener.onDisplayChanged(DISPLAY_ID);
2374         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2375         assertNull(vote);
2376     }
2377 
2378     @Test
testHbmVoting_HbmUnsupported()2379     public void testHbmVoting_HbmUnsupported() {
2380         DisplayModeDirector director =
2381                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2382         director.start(createMockSensorManager());
2383 
2384         ArgumentCaptor<DisplayListener> captor =
2385                   ArgumentCaptor.forClass(DisplayListener.class);
2386         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2387                   eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2388                   | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2389         DisplayListener listener = captor.getValue();
2390 
2391         // Specify Limitation
2392         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
2393                 List.of(new RefreshRateLimitation(
2394                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
2395                         60.0f, 60.0f)));
2396 
2397         // Verify that there is no HBM vote initially
2398         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2399         assertNull(vote);
2400 
2401         // Turn on HBM when HBM is supported; expect a valid transition point and a vote.
2402         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2403                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2404                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2405         listener.onDisplayChanged(DISPLAY_ID);
2406         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2407         assertVoteForPhysicalRefreshRate(vote, 60.0f);
2408 
2409         // Turn off HBM
2410         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2411                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2412                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2413         listener.onDisplayChanged(DISPLAY_ID);
2414         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2415         assertNull(vote);
2416 
2417         // Turn on Sunlight HBM when HBM is unsupported; expect an invalid transition point and
2418         // no vote.
2419         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2420                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2421                     HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2422         listener.onDisplayChanged(DISPLAY_ID);
2423         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2424         assertNull(vote);
2425 
2426         // Turn on HDR HBM when HBM is unsupported; expect an invalid transition point and
2427         // no vote.
2428         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2429                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
2430                     HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2431         listener.onDisplayChanged(DISPLAY_ID);
2432         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2433         assertNull(vote);
2434 
2435         // Turn off HBM
2436         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2437                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
2438                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2439         listener.onDisplayChanged(DISPLAY_ID);
2440         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2441         assertNull(vote);
2442     }
2443 
setHbmAndAssertRefreshRate( DisplayModeDirector director, DisplayListener listener, int mode, float rr)2444     private void setHbmAndAssertRefreshRate(
2445             DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
2446         when(mInjector.getBrightnessInfo(DISPLAY_ID))
2447                 .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT,
2448                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2449         listener.onDisplayChanged(DISPLAY_ID);
2450 
2451         final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2452         if (Float.isNaN(rr)) {
2453             assertNull(vote);
2454         } else {
2455             assertVoteForPhysicalRefreshRate(vote, rr);
2456         }
2457     }
2458 
2459     @Test
testHbmVoting_forSunlightAndHdr()2460     public void testHbmVoting_forSunlightAndHdr() {
2461         DisplayModeDirector director =
2462                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2463 
2464         // Specify HDR limitation before starting DisplayModeDirector to avoid waiting on property
2465         // propagation
2466         final int hdrRr = 60;
2467         mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hdrRr);
2468         director.start(createMockSensorManager());
2469 
2470         ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class);
2471         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2472                 eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2473                         | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2474         DisplayListener listener = captor.getValue();
2475 
2476         // Specify Sunlight limitations
2477         final float sunlightRr = 90.0f;
2478         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID))
2479                 .thenReturn(List.of(new RefreshRateLimitation(
2480                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, sunlightRr,
2481                         sunlightRr)));
2482 
2483         // Verify that there is no HBM vote initially
2484         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2485         assertNull(vote);
2486 
2487         // Verify all state transitions
2488         setHbmAndAssertRefreshRate(
2489                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
2490         setHbmAndAssertRefreshRate(
2491                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
2492         setHbmAndAssertRefreshRate(
2493                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
2494         setHbmAndAssertRefreshRate(
2495                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
2496         setHbmAndAssertRefreshRate(
2497                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
2498         setHbmAndAssertRefreshRate(
2499                 director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
2500     }
2501 
2502     @Test
testHbmVoting_RemovedDisplay()2503     public void testHbmVoting_RemovedDisplay() {
2504         DisplayModeDirector director =
2505                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2506         director.start(createMockSensorManager());
2507 
2508         ArgumentCaptor<DisplayListener> captor =
2509                   ArgumentCaptor.forClass(DisplayListener.class);
2510         verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
2511                   eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2512                   | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
2513         DisplayListener listener = captor.getValue();
2514 
2515         // Specify Limitation for different display
2516         when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
2517                 List.of(new RefreshRateLimitation(
2518                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
2519                         60.f, 60.f)));
2520 
2521         // Verify that there is no HBM vote initially
2522         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2523         assertNull(vote);
2524 
2525         // Turn on HBM
2526         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
2527                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
2528                     TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
2529         listener.onDisplayChanged(DISPLAY_ID);
2530         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2531         assertVoteForPhysicalRefreshRate(vote, 60.f);
2532 
2533         // Turn off HBM
2534         listener.onDisplayRemoved(DISPLAY_ID);
2535         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
2536         assertNull(vote);
2537     }
2538 
2539     @Test
testSkinTemperature()2540     public void testSkinTemperature() throws RemoteException {
2541         DisplayModeDirector director =
2542                 createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
2543         director.start(createMockSensorManager());
2544 
2545         ArgumentCaptor<IThermalEventListener> thermalEventListener =
2546                 ArgumentCaptor.forClass(IThermalEventListener.class);
2547 
2548         verify(mInjector).registerThermalServiceListener(thermalEventListener.capture());
2549         final IThermalEventListener listener = thermalEventListener.getValue();
2550 
2551         // Verify that there is no skin temperature vote initially.
2552         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
2553         assertNull(vote);
2554 
2555         // Set the skin temperature to critical and verify that we added a vote.
2556         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
2557         BackgroundThread.getHandler().runWithScissors(() -> { }, 500 /*timeout*/);
2558         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
2559         assertVoteForRenderFrameRateRange(vote, 0f, 60.f);
2560 
2561         // Set the skin temperature to severe and verify that the vote is gone.
2562         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
2563         BackgroundThread.getHandler().runWithScissors(() -> { }, 500 /*timeout*/);
2564         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
2565         assertNull(vote);
2566     }
2567 
2568     @Test
testNotifyDefaultDisplayDeviceUpdated()2569     public void testNotifyDefaultDisplayDeviceUpdated() {
2570         Resources resources = mock(Resources.class);
2571         when(mContext.getResources()).thenReturn(resources);
2572         when(resources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate))
2573             .thenReturn(75);
2574         when(resources.getInteger(R.integer.config_defaultRefreshRate))
2575             .thenReturn(45);
2576         when(resources.getInteger(R.integer.config_fixedRefreshRateInHighZone))
2577             .thenReturn(65);
2578         when(resources.getInteger(R.integer.config_defaultRefreshRateInZone))
2579             .thenReturn(85);
2580         when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmHdr))
2581             .thenReturn(95);
2582         when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmSunlight))
2583             .thenReturn(100);
2584         when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
2585             .thenReturn(new int[]{5});
2586         when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
2587             .thenReturn(new int[]{10});
2588         when(
2589             resources.getIntArray(R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate))
2590             .thenReturn(new int[]{250});
2591         when(
2592             resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
2593             .thenReturn(new int[]{7000});
2594         when(resources.getInteger(
2595             com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
2596             .thenReturn(3);
2597         ArgumentCaptor<TypedValue> valueArgumentCaptor = ArgumentCaptor.forClass(TypedValue.class);
2598         doAnswer((Answer<Void>) invocation -> {
2599             valueArgumentCaptor.getValue().type = 4;
2600             valueArgumentCaptor.getValue().data = 13;
2601             return null;
2602         }).when(resources).getValue(eq(com.android.internal.R.dimen
2603                 .config_displayWhiteBalanceBrightnessFilterIntercept),
2604                 valueArgumentCaptor.capture(), eq(true));
2605         DisplayModeDirector director =
2606                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2607         SensorManager sensorManager = createMockSensorManager(createLightSensor());
2608         director.start(sensorManager);
2609         // We don't expect any interaction with DeviceConfig when the director is initialized
2610         // because we explicitly avoid doing this as this can lead to a latency spike in the
2611         // startup of DisplayManagerService
2612         // Verify all the loaded values are from config.xml
2613         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0);
2614         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75,
2615                 0.0);
2616         assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
2617         assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 85);
2618         assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 95);
2619         assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 100);
2620         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
2621                 new float[]{ BrightnessSynchronizer.brightnessIntToFloat(250) }, /* delta= */ 0);
2622         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
2623                 new float[]{7000}, /* delta= */ 0);
2624         assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
2625                 new float[]{ BrightnessSynchronizer.brightnessIntToFloat(5) }, /* delta= */ 0);
2626         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
2627                 new float[]{10}, /* delta= */ 0);
2628 
2629 
2630         // Notify that the default display is updated, such that DisplayDeviceConfig has new values
2631         DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
2632         when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
2633         when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
2634         when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(60);
2635         when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(65);
2636         when(displayDeviceConfig.getLowDisplayBrightnessThresholds())
2637                 .thenReturn(new float[]{0.025f});
2638         when(displayDeviceConfig.getLowAmbientBrightnessThresholds())
2639                 .thenReturn(new float[]{30});
2640         when(displayDeviceConfig.getHighDisplayBrightnessThresholds())
2641                 .thenReturn(new float[]{0.21f});
2642         when(displayDeviceConfig.getHighAmbientBrightnessThresholds())
2643                 .thenReturn(new float[]{2100});
2644         when(displayDeviceConfig.getDefaultRefreshRateInHbmHdr()).thenReturn(65);
2645         when(displayDeviceConfig.getDefaultRefreshRateInHbmSunlight()).thenReturn(75);
2646         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
2647 
2648         // Verify the new values are from the freshly loaded DisplayDeviceConfig.
2649         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
2650         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 65,
2651                 0.0);
2652         assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55);
2653         assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50);
2654         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
2655                 new float[]{0.21f}, /* delta= */ 0);
2656         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
2657                 new float[]{2100}, /* delta= */ 0);
2658         assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
2659                 new float[]{0.025f}, /* delta= */ 0);
2660         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
2661                 new float[]{30}, /* delta= */ 0);
2662         assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
2663         assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
2664 
2665         // Notify that the default display is updated, such that DeviceConfig has new values
2666         FakeDeviceConfig config = mInjector.getDeviceConfig();
2667         config.setDefaultPeakRefreshRate(60);
2668         config.setRefreshRateInHighZone(65);
2669         config.setRefreshRateInLowZone(70);
2670         config.setLowAmbientBrightnessThresholds(new int[]{20});
2671         config.setLowDisplayBrightnessThresholds(new int[]{10});
2672         config.setHighDisplayBrightnessThresholds(new int[]{255});
2673         config.setHighAmbientBrightnessThresholds(new int[]{8000});
2674         config.setRefreshRateInHbmHdr(70);
2675         config.setRefreshRateInHbmSunlight(80);
2676         // Need to wait for the property change to propagate to the main thread.
2677         waitForIdleSync();
2678 
2679         // Verify the values are loaded from the DeviceConfig.
2680         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
2681         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60,
2682                 0.0);
2683         assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
2684         assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 70);
2685         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
2686                 new float[]{ BrightnessSynchronizer.brightnessIntToFloat(255) }, /* delta= */ 0);
2687         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
2688                 new float[]{8000}, /* delta= */ 0);
2689         assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
2690                 new float[]{ BrightnessSynchronizer.brightnessIntToFloat(10) }, /* delta= */ 0);
2691         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
2692                 new float[]{20}, /* delta= */ 0);
2693         assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 70);
2694         assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 80);
2695 
2696         // Reset the DeviceConfig
2697         config.setDefaultPeakRefreshRate(null);
2698         config.setRefreshRateInHighZone(null);
2699         config.setRefreshRateInLowZone(null);
2700         config.setLowAmbientBrightnessThresholds(new int[]{});
2701         config.setLowDisplayBrightnessThresholds(new int[]{});
2702         config.setHighDisplayBrightnessThresholds(new int[]{});
2703         config.setHighAmbientBrightnessThresholds(new int[]{});
2704         config.setRefreshRateInHbmHdr(null);
2705         config.setRefreshRateInHbmSunlight(null);
2706         waitForIdleSync();
2707 
2708         // verify the new values now fallback to DisplayDeviceConfig
2709         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
2710         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 65,
2711                 0.0);
2712         assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55);
2713         assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50);
2714         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThresholds(),
2715                 new float[]{0.21f}, /* delta= */ 0);
2716         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThresholds(),
2717                 new float[]{2100}, /* delta= */ 0);
2718         assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThresholds(),
2719                 new float[]{0.025f}, /* delta= */ 0);
2720         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
2721                 new float[]{30}, /* delta= */ 0);
2722         assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
2723         assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
2724     }
2725 
2726     @Test
testSensorReloadOnDeviceSwitch()2727     public void testSensorReloadOnDeviceSwitch() throws Exception {
2728         // First, configure brightness zones or DMD won't register for sensor data.
2729         final FakeDeviceConfig config = mInjector.getDeviceConfig();
2730         config.setRefreshRateInHighZone(60);
2731         config.setHighDisplayBrightnessThresholds(new int[] { 255 });
2732         config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
2733 
2734         DisplayModeDirector director =
2735                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
2736         setPeakRefreshRate(90 /*fps*/);
2737         director.getSettingsObserver().setDefaultRefreshRate(90);
2738         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
2739 
2740         Sensor lightSensorOne = TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
2741         Sensor lightSensorTwo = TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
2742         SensorManager sensorManager = createMockSensorManager(lightSensorOne, lightSensorTwo);
2743         when(sensorManager.getDefaultSensor(5)).thenReturn(lightSensorOne, lightSensorTwo);
2744         director.start(sensorManager);
2745         ArgumentCaptor<SensorEventListener> listenerCaptor =
2746                 ArgumentCaptor.forClass(SensorEventListener.class);
2747         verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
2748                 .registerListener(
2749                         listenerCaptor.capture(),
2750                         eq(lightSensorOne),
2751                         anyInt(),
2752                         any(Handler.class));
2753 
2754         DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
2755         when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
2756         when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
2757         when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[]{0.025f});
2758         when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[]{30});
2759         when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[]{0.21f});
2760         when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[]{2100});
2761 
2762         Resources resMock = mock(Resources.class);
2763         when(resMock.getInteger(
2764                 com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
2765                 .thenReturn(3);
2766         ArgumentCaptor<TypedValue> valueArgumentCaptor = ArgumentCaptor.forClass(TypedValue.class);
2767         doAnswer((Answer<Void>) invocation -> {
2768             valueArgumentCaptor.getValue().type = 4;
2769             valueArgumentCaptor.getValue().data = 13;
2770             return null;
2771         }).when(resMock).getValue(anyInt(), valueArgumentCaptor.capture(), eq(true));
2772         when(mContext.getResources()).thenReturn(resMock);
2773 
2774         director.defaultDisplayDeviceUpdated(ddcMock);
2775 
2776         verify(sensorManager).unregisterListener(any(SensorEventListener.class));
2777         verify(sensorManager).registerListener(any(SensorEventListener.class),
2778                 eq(lightSensorTwo), anyInt(), any(Handler.class));
2779     }
2780 
2781     @Test
testAuthenticationPossibleSetsPhysicalRateRangesToMax()2782     public void testAuthenticationPossibleSetsPhysicalRateRangesToMax() throws RemoteException {
2783         DisplayModeDirector director =
2784                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2785         // don't call director.start(createMockSensorManager());
2786         // DisplayObserver will reset mSupportedModesByDisplay
2787         director.onBootCompleted();
2788         ArgumentCaptor<IUdfpsRefreshRateRequestCallback> captor =
2789                 ArgumentCaptor.forClass(IUdfpsRefreshRateRequestCallback.class);
2790         verify(mStatusBarMock).setUdfpsRefreshRateCallback(captor.capture());
2791 
2792         captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
2793 
2794         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
2795         assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
2796         assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
2797     }
2798 
2799     @Test
testAuthenticationPossibleUnsetsVote()2800     public void testAuthenticationPossibleUnsetsVote() throws RemoteException {
2801         DisplayModeDirector director =
2802                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2803         director.start(createMockSensorManager());
2804         director.onBootCompleted();
2805         ArgumentCaptor<IUdfpsRefreshRateRequestCallback> captor =
2806                 ArgumentCaptor.forClass(IUdfpsRefreshRateRequestCallback.class);
2807         verify(mStatusBarMock).setUdfpsRefreshRateCallback(captor.capture());
2808         captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
2809         captor.getValue().onAuthenticationPossible(DISPLAY_ID, false);
2810 
2811         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
2812         assertNull(vote);
2813     }
2814 
2815     @Test
testUdfpsRequestSetsPhysicalRateRangesToMax()2816     public void testUdfpsRequestSetsPhysicalRateRangesToMax() throws RemoteException {
2817         DisplayModeDirector director =
2818                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2819         // don't call director.start(createMockSensorManager());
2820         // DisplayObserver will reset mSupportedModesByDisplay
2821         director.onBootCompleted();
2822         ArgumentCaptor<IUdfpsRefreshRateRequestCallback> captor =
2823                 ArgumentCaptor.forClass(IUdfpsRefreshRateRequestCallback.class);
2824         verify(mStatusBarMock).setUdfpsRefreshRateCallback(captor.capture());
2825 
2826         captor.getValue().onRequestEnabled(DISPLAY_ID);
2827 
2828         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
2829         assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
2830         assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
2831     }
2832 
2833     @Test
testUdfpsRequestUnsetsUnsetsVote()2834     public void testUdfpsRequestUnsetsUnsetsVote() throws RemoteException {
2835         DisplayModeDirector director =
2836                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2837         director.start(createMockSensorManager());
2838         director.onBootCompleted();
2839         ArgumentCaptor<IUdfpsRefreshRateRequestCallback> captor =
2840                 ArgumentCaptor.forClass(IUdfpsRefreshRateRequestCallback.class);
2841         verify(mStatusBarMock).setUdfpsRefreshRateCallback(captor.capture());
2842         captor.getValue().onRequestEnabled(DISPLAY_ID);
2843         captor.getValue().onRequestDisabled(DISPLAY_ID);
2844 
2845         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
2846         assertNull(vote);
2847     }
2848 
2849     @Test
testUpdateLayoutLimitedRefreshRate_validDisplayInfo()2850     public void testUpdateLayoutLimitedRefreshRate_validDisplayInfo() {
2851         DisplayModeDirector director =
2852                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2853         director.start(createMockSensorManager());
2854 
2855         ArgumentCaptor<DisplayListener> displayListenerCaptor =
2856                 ArgumentCaptor.forClass(DisplayListener.class);
2857         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
2858                 any(Handler.class));
2859         DisplayListener displayListener = displayListenerCaptor.getValue();
2860 
2861         float refreshRate = 60;
2862         mInjector.mDisplayInfo.layoutLimitedRefreshRate =
2863                 new RefreshRateRange(refreshRate, refreshRate);
2864         displayListener.onDisplayChanged(DISPLAY_ID);
2865 
2866         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
2867         assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate);
2868 
2869         mInjector.mDisplayInfo.layoutLimitedRefreshRate = null;
2870         displayListener.onDisplayChanged(DISPLAY_ID);
2871 
2872         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
2873         assertNull(vote);
2874     }
2875 
2876     @Test
testUpdateLayoutLimitedRefreshRate_invalidDisplayInfo()2877     public void testUpdateLayoutLimitedRefreshRate_invalidDisplayInfo() {
2878         DisplayModeDirector director =
2879                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
2880         director.start(createMockSensorManager());
2881 
2882         ArgumentCaptor<DisplayListener> displayListenerCaptor =
2883                 ArgumentCaptor.forClass(DisplayListener.class);
2884         verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
2885                 any(Handler.class));
2886         DisplayListener displayListener = displayListenerCaptor.getValue();
2887 
2888         mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10);
2889         mInjector.mDisplayInfoValid = false;
2890         displayListener.onDisplayChanged(DISPLAY_ID);
2891 
2892         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
2893         assertNull(vote);
2894     }
2895 
getSkinTemp(@emperature.ThrottlingStatus int status)2896     private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
2897         return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
2898     }
2899 
assertVoteForPhysicalRefreshRate(Vote vote, float refreshRate)2900     private void assertVoteForPhysicalRefreshRate(Vote vote, float refreshRate) {
2901         assertThat(vote).isNotNull();
2902         final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
2903         assertThat(vote.refreshRateRanges.physical).isEqualTo(expectedRange);
2904     }
2905 
assertVoteForRenderFrameRateRange( Vote vote, float frameRateLow, float frameRateHigh)2906     private void assertVoteForRenderFrameRateRange(
2907             Vote vote, float frameRateLow, float frameRateHigh) {
2908         assertThat(vote).isNotNull();
2909         final RefreshRateRange expectedRange =
2910                 new RefreshRateRange(frameRateLow, frameRateHigh);
2911         assertThat(vote.refreshRateRanges.render).isEqualTo(expectedRange);
2912     }
2913 
2914     public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
2915         @Override
getProperty(String namespace, String name)2916         public String getProperty(String namespace, String name) {
2917             Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
2918             return super.getProperty(namespace, name);
2919         }
2920 
2921         @Override
addOnPropertiesChangedListener( String namespace, Executor executor, DeviceConfig.OnPropertiesChangedListener listener)2922         public void addOnPropertiesChangedListener(
2923                 String namespace,
2924                 Executor executor,
2925                 DeviceConfig.OnPropertiesChangedListener listener) {
2926             Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
2927             super.addOnPropertiesChangedListener(namespace, executor, listener);
2928         }
2929 
setRefreshRateInLowZone(Integer fps)2930         void setRefreshRateInLowZone(Integer fps) {
2931             putPropertyAndNotify(
2932                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
2933                     String.valueOf(fps));
2934         }
2935 
setRefreshRateInHbmSunlight(Integer fps)2936         void setRefreshRateInHbmSunlight(Integer fps) {
2937             putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2938                     KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps));
2939         }
2940 
setRefreshRateInHbmHdr(Integer fps)2941         void setRefreshRateInHbmHdr(Integer fps) {
2942             putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2943                     KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
2944         }
2945 
setThermalBrightnessThrottlingData(String brightnessThrottlingData)2946         public void setThermalBrightnessThrottlingData(String brightnessThrottlingData) {
2947             putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2948                     KEY_BRIGHTNESS_THROTTLING_DATA, brightnessThrottlingData);
2949         }
2950 
setLowDisplayBrightnessThresholds(int[] brightnessThresholds)2951         void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
2952             String thresholds = toPropertyValue(brightnessThresholds);
2953 
2954             if (DEBUG) {
2955                 Slog.e(TAG, "Brightness Thresholds = " + thresholds);
2956             }
2957 
2958             putPropertyAndNotify(
2959                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2960                     KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
2961                     thresholds);
2962         }
2963 
setLowAmbientBrightnessThresholds(int[] ambientThresholds)2964         void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
2965             String thresholds = toPropertyValue(ambientThresholds);
2966 
2967             if (DEBUG) {
2968                 Slog.e(TAG, "Ambient Thresholds = " + thresholds);
2969             }
2970 
2971             putPropertyAndNotify(
2972                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2973                     KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
2974                     thresholds);
2975         }
2976 
setRefreshRateInHighZone(Integer fps)2977         void setRefreshRateInHighZone(Integer fps) {
2978             putPropertyAndNotify(
2979                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
2980                     String.valueOf(fps));
2981         }
2982 
setDefaultPeakRefreshRate(Integer fps)2983         void setDefaultPeakRefreshRate(Integer fps) {
2984             putPropertyAndNotify(
2985                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_PEAK_REFRESH_RATE_DEFAULT,
2986                     String.valueOf(fps));
2987         }
2988 
setHighDisplayBrightnessThresholds(int[] brightnessThresholds)2989         void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
2990             String thresholds = toPropertyValue(brightnessThresholds);
2991 
2992             if (DEBUG) {
2993                 Slog.e(TAG, "Brightness Thresholds = " + thresholds);
2994             }
2995 
2996             putPropertyAndNotify(
2997                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2998                     KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
2999                     thresholds);
3000         }
3001 
setHighAmbientBrightnessThresholds(int[] ambientThresholds)3002         void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
3003             String thresholds = toPropertyValue(ambientThresholds);
3004 
3005             if (DEBUG) {
3006                 Slog.e(TAG, "Ambient Thresholds = " + thresholds);
3007             }
3008 
3009             putPropertyAndNotify(
3010                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
3011                     KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
3012                     thresholds);
3013         }
3014 
3015         @NonNull
toPropertyValue(@onNull int[] intArray)3016         private static String toPropertyValue(@NonNull int[] intArray) {
3017             return Arrays.stream(intArray)
3018                     .mapToObj(Integer::toString)
3019                     .collect(Collectors.joining(","));
3020         }
3021     }
3022 
setBrightness(int brightness, int adjustedBrightness, DisplayListener listener)3023     private void setBrightness(int brightness, int adjustedBrightness, DisplayListener listener) {
3024         float floatBri = BrightnessSynchronizer.brightnessIntToFloat(brightness);
3025         float floatAdjBri = BrightnessSynchronizer.brightnessIntToFloat(adjustedBrightness);
3026 
3027         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
3028                 new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
3029                     BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT,
3030                     BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
3031         listener.onDisplayChanged(DISPLAY_ID);
3032     }
3033 
setPeakRefreshRate(float fps)3034     private void setPeakRefreshRate(float fps) {
3035         Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
3036                  fps);
3037         mInjector.notifyPeakRefreshRateChanged();
3038         waitForIdleSync();
3039     }
3040 
createMockSensorManager(Sensor... sensors)3041     private static SensorManager createMockSensorManager(Sensor... sensors) {
3042         SensorManager sensorManager = mock(SensorManager.class);
3043         when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
3044             List<Sensor> requestedSensors = new ArrayList<>();
3045             int type = invocation.getArgument(0);
3046             for (Sensor sensor : sensors) {
3047                 if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
3048                     requestedSensors.add(sensor);
3049                 }
3050             }
3051             return requestedSensors;
3052         });
3053 
3054         when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
3055             int type = invocation.getArgument(0);
3056             for (Sensor sensor : sensors) {
3057                 if (sensor.getType() == type) {
3058                     return sensor;
3059                 }
3060             }
3061             return null;
3062         });
3063         return sensorManager;
3064     }
3065 
createLightSensor()3066     private static Sensor createLightSensor() {
3067         try {
3068             return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
3069         } catch (Exception e) {
3070             // There's nothing we can do if this fails, just throw a RuntimeException so that we
3071             // don't have to mark every function that might call this as throwing Exception
3072             throw new RuntimeException("Failed to create a light sensor", e);
3073         }
3074     }
3075 
waitForIdleSync()3076     private void waitForIdleSync() {
3077         mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
3078     }
3079 
3080     public static class FakesInjector implements DisplayModeDirector.Injector {
3081         private final FakeDeviceConfig mDeviceConfig;
3082         private final DisplayInfo mDisplayInfo;
3083         private final Display mDisplay;
3084         private boolean mDisplayInfoValid = true;
3085         private ContentObserver mBrightnessObserver;
3086         private ContentObserver mPeakRefreshRateObserver;
3087 
FakesInjector()3088         FakesInjector() {
3089             mDeviceConfig = new FakeDeviceConfig();
3090             mDisplayInfo = new DisplayInfo();
3091             mDisplayInfo.defaultModeId = MODE_ID;
3092             mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
3093                     800, 600, /* refreshRate= */ 60)};
3094             mDisplay = createDisplay(DISPLAY_ID);
3095         }
3096 
3097         @NonNull
getDeviceConfig()3098         public FakeDeviceConfig getDeviceConfig() {
3099             return mDeviceConfig;
3100         }
3101 
3102         @Override
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)3103         public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
3104                 @NonNull ContentObserver observer) {
3105             mPeakRefreshRateObserver = observer;
3106         }
3107 
3108         @Override
registerDisplayListener(DisplayListener listener, Handler handler)3109         public void registerDisplayListener(DisplayListener listener, Handler handler) {}
3110 
3111         @Override
registerDisplayListener(DisplayListener listener, Handler handler, long flag)3112         public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
3113 
3114         @Override
getDisplay(int displayId)3115         public Display getDisplay(int displayId) {
3116             return mDisplay;
3117         }
3118 
3119         @Override
getDisplays()3120         public Display[] getDisplays() {
3121             return new Display[] { mDisplay };
3122         }
3123 
3124         @Override
getDisplayInfo(int displayId, DisplayInfo displayInfo)3125         public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
3126             displayInfo.copyFrom(mDisplayInfo);
3127             return mDisplayInfoValid;
3128         }
3129 
3130         @Override
getBrightnessInfo(int displayId)3131         public BrightnessInfo getBrightnessInfo(int displayId) {
3132             return null;
3133         }
3134 
3135         @Override
isDozeState(Display d)3136         public boolean isDozeState(Display d) {
3137             return false;
3138         }
3139 
3140         @Override
registerThermalServiceListener(IThermalEventListener listener)3141         public boolean registerThermalServiceListener(IThermalEventListener listener) {
3142             return true;
3143         }
3144 
3145         @Override
unregisterThermalServiceListener(IThermalEventListener listener)3146         public void unregisterThermalServiceListener(IThermalEventListener listener) {
3147         }
3148 
3149         @Override
supportsFrameRateOverride()3150         public boolean supportsFrameRateOverride() {
3151             return true;
3152         }
3153 
createDisplay(int id)3154         protected Display createDisplay(int id) {
3155             return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
3156                     ApplicationProvider.getApplicationContext().getResources());
3157         }
3158 
notifyPeakRefreshRateChanged()3159         void notifyPeakRefreshRateChanged() {
3160             if (mPeakRefreshRateObserver != null) {
3161                 mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
3162                         PEAK_REFRESH_RATE_URI);
3163             }
3164         }
3165     }
3166 }
3167