1 /* 2 * Copyright 2022 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; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.mockito.Mockito.eq; 21 import static org.mockito.Mockito.isA; 22 import static org.mockito.Mockito.spy; 23 import static org.mockito.Mockito.times; 24 import static org.mockito.Mockito.verify; 25 import static org.mockito.Mockito.when; 26 27 import android.content.Context; 28 import android.content.ContextWrapper; 29 import android.database.ContentObserver; 30 import android.hardware.display.DisplayManager; 31 import android.hardware.display.DisplayManager.DisplayListener; 32 import android.net.Uri; 33 import android.os.Handler; 34 import android.os.UserHandle; 35 import android.os.test.TestLooper; 36 import android.provider.Settings; 37 import android.test.mock.MockContentResolver; 38 import android.view.Display; 39 40 import androidx.test.core.app.ApplicationProvider; 41 import androidx.test.filters.SmallTest; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import com.android.internal.display.BrightnessSynchronizer; 45 import com.android.internal.util.test.FakeSettingsProvider; 46 import com.android.server.testutils.OffsettableClock; 47 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.ArgumentCaptor; 52 import org.mockito.Captor; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 @SmallTest 57 @RunWith(AndroidJUnit4.class) 58 public class BrightnessSynchronizerTest { 59 private static final float EPSILON = 0.00001f; 60 private static final Uri BRIGHTNESS_URI = 61 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); 62 63 private Context mContext; 64 private MockContentResolver mContentResolverSpy; 65 private OffsettableClock mClock; 66 private DisplayListener mDisplayListener; 67 private ContentObserver mContentObserver; 68 private TestLooper mTestLooper; 69 70 @Mock private DisplayManager mDisplayManagerMock; 71 @Captor private ArgumentCaptor<DisplayListener> mDisplayListenerCaptor; 72 @Captor private ArgumentCaptor<ContentObserver> mContentObserverCaptor; 73 74 @Before setUp()75 public void setUp() throws Exception { 76 MockitoAnnotations.initMocks(this); 77 mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); 78 mContentResolverSpy = spy(new MockContentResolver(mContext)); 79 mContentResolverSpy.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 80 when(mContext.getContentResolver()).thenReturn(mContentResolverSpy); 81 when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManagerMock); 82 mClock = new OffsettableClock.Stopped(); 83 mTestLooper = new TestLooper(mClock::now); 84 } 85 86 @Test testSetFloat()87 public void testSetFloat() throws Exception { 88 putFloatSetting(0.5f); 89 putIntSetting(128); 90 start(); 91 92 // Set float brightness to 0.4 93 putFloatSetting(0.4f); 94 advanceTime(10); 95 verifyIntWasSetTo(fToI(0.4f)); 96 } 97 98 @Test testSetInt()99 public void testSetInt() { 100 putFloatSetting(0.5f); 101 putIntSetting(128); 102 start(); 103 104 // Set int brightness to 64 105 putIntSetting(64); 106 advanceTime(10); 107 verifyFloatWasSetTo(iToF(64)); 108 } 109 110 @Test testSetIntQuickSuccession()111 public void testSetIntQuickSuccession() { 112 putFloatSetting(0.5f); 113 putIntSetting(128); 114 start(); 115 116 putIntSetting(50); 117 putIntSetting(40); 118 advanceTime(10); 119 120 verifyFloatWasSetTo(iToF(50)); 121 122 // now confirm the first value (via callback) so that we can process the second one. 123 putFloatSetting(iToF(50)); 124 advanceTime(10); 125 verifyFloatWasSetTo(iToF(40)); 126 } 127 128 @Test testSetSameIntValue_nothingUpdated()129 public void testSetSameIntValue_nothingUpdated() { 130 putFloatSetting(0.5f); 131 putIntSetting(128); 132 start(); 133 134 putIntSetting(128); 135 advanceTime(10); 136 verify(mDisplayManagerMock, times(0)).setBrightness( 137 eq(Display.DEFAULT_DISPLAY), eq(iToF(128))); 138 } 139 140 @Test testUpdateDuringResponseIsNotOverwritten()141 public void testUpdateDuringResponseIsNotOverwritten() { 142 putFloatSetting(0.5f); 143 putIntSetting(128); 144 start(); 145 146 // First, change the float to 0.4f 147 putFloatSetting(0.4f); 148 advanceTime(10); 149 150 // Now set the int to something else (not equal to 0.4f) 151 putIntSetting(20); 152 advanceTime(10); 153 154 // Verify that this update did not get sent to float, because synchronizer 155 // is still waiting for confirmation of its first value. 156 verify(mDisplayManagerMock, times(0)).setBrightness( 157 eq(Display.DEFAULT_DISPLAY), eq(iToF(20))); 158 159 // Send the confirmation of the initial change. This should trigger the new value to 160 // finally be processed and we can verify that the new value (20) is sent. 161 putIntSetting(fToI(0.4f)); 162 advanceTime(10); 163 verify(mDisplayManagerMock).setBrightness( 164 eq(Display.DEFAULT_DISPLAY), eq(iToF(20))); 165 166 } 167 168 @Test testSetFloat_outOfTimeForResponse()169 public void testSetFloat_outOfTimeForResponse() { 170 putFloatSetting(0.5f); 171 putIntSetting(128); 172 start(); 173 advanceTime(210); 174 175 // First, change the float to 0.4f 176 putFloatSetting(0.4f); 177 advanceTime(10); 178 179 // Now set the int to something else (not equal to 0.4f) 180 putIntSetting(20); 181 182 // Now, go beyond the timeout so that the last 20 event gets executed. 183 advanceTime(200); 184 185 // Verify that the new value gets sent because the timeout expired. 186 verify(mDisplayManagerMock).setBrightness( 187 eq(Display.DEFAULT_DISPLAY), eq(iToF(20))); 188 189 // Send a confirmation of the initial event, BrightnessSynchronizer should treat this as a 190 // new event because the timeout had already expired 191 putIntSetting(fToI(0.4f)); 192 // Because the previous setting will be treated as a new event, we actually want to send 193 // confirmation of the setBrightness() we just verified so that it can be executed as well. 194 putFloatSetting(iToF(20)); 195 advanceTime(10); 196 197 // Verify we sent what would have been the confirmation as a new event to displaymanager. 198 // We do both fToI and iToF because the conversions are not symmetric. 199 verify(mDisplayManagerMock).setBrightness( 200 eq(Display.DEFAULT_DISPLAY), eq(iToF(fToI(0.4f)))); 201 } 202 start()203 private BrightnessSynchronizer start() { 204 BrightnessSynchronizer bs = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(), 205 mClock::now); 206 bs.startSynchronizing(); 207 verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(), 208 isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); 209 mDisplayListener = mDisplayListenerCaptor.getValue(); 210 211 verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false), 212 mContentObserverCaptor.capture(), eq(UserHandle.USER_ALL)); 213 mContentObserver = mContentObserverCaptor.getValue(); 214 return bs; 215 } 216 getIntSetting()217 private int getIntSetting() throws Exception { 218 return Settings.System.getInt(mContentResolverSpy, Settings.System.SCREEN_BRIGHTNESS); 219 } 220 putIntSetting(int brightness)221 private void putIntSetting(int brightness) { 222 Settings.System.putInt(mContentResolverSpy, Settings.System.SCREEN_BRIGHTNESS, brightness); 223 if (mContentObserver != null) { 224 mContentObserver.onChange(false /*=selfChange*/, BRIGHTNESS_URI); 225 } 226 } 227 putFloatSetting(float brightness)228 private void putFloatSetting(float brightness) { 229 when(mDisplayManagerMock.getBrightness(eq(Display.DEFAULT_DISPLAY))).thenReturn(brightness); 230 if (mDisplayListener != null) { 231 mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY); 232 } 233 } 234 verifyIntWasSetTo(int brightness)235 private void verifyIntWasSetTo(int brightness) throws Exception { 236 assertEquals(brightness, getIntSetting()); 237 } 238 verifyFloatWasSetTo(float brightness)239 private void verifyFloatWasSetTo(float brightness) { 240 verify(mDisplayManagerMock).setBrightness(eq(Display.DEFAULT_DISPLAY), eq(brightness)); 241 } 242 fToI(float brightness)243 private int fToI(float brightness) { 244 return BrightnessSynchronizer.brightnessFloatToInt(brightness); 245 } 246 iToF(int brightness)247 private float iToF(int brightness) { 248 return BrightnessSynchronizer.brightnessIntToFloat(brightness); 249 } 250 advanceTime(long timeMs)251 private void advanceTime(long timeMs) { 252 mClock.fastForward(timeMs); 253 mTestLooper.dispatchAll(); 254 } 255 } 256