1 /* <lambda>null2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.input 18 19 import android.content.Context 20 import android.content.ContextWrapper 21 import android.content.res.Resources 22 import android.hardware.Sensor 23 import android.hardware.SensorEvent 24 import android.hardware.SensorEventListener 25 import android.hardware.SensorManager 26 import android.hardware.display.DisplayManagerInternal 27 import android.hardware.input.InputSensorInfo 28 import android.os.Handler 29 import android.os.test.TestLooper 30 import android.platform.test.annotations.Presubmit 31 import android.util.TypedValue 32 import android.view.Display 33 import android.view.DisplayInfo 34 import androidx.test.core.app.ApplicationProvider 35 import com.android.internal.R 36 import com.android.server.LocalServices 37 import com.android.server.input.AmbientKeyboardBacklightController.HYSTERESIS_THRESHOLD 38 import org.junit.Assert.assertEquals 39 import org.junit.Assert.assertFalse 40 import org.junit.Assert.assertThrows 41 import org.junit.Assert.assertTrue 42 import org.junit.Before 43 import org.junit.Rule 44 import org.junit.Test 45 import org.mockito.Mock 46 import org.mockito.Mockito.any 47 import org.mockito.Mockito.anyBoolean 48 import org.mockito.Mockito.anyInt 49 import org.mockito.Mockito.eq 50 import org.mockito.Mockito.spy 51 import org.mockito.Mockito.`when` 52 import org.mockito.junit.MockitoJUnit 53 54 /** 55 * Tests for {@link AmbientKeyboardBacklightController}. 56 * 57 * Build/Install/Run: 58 * atest InputTests:AmbientKeyboardBacklightControllerTests 59 */ 60 @Presubmit 61 class AmbientKeyboardBacklightControllerTests { 62 63 companion object { 64 const val DEFAULT_DISPLAY_UNIQUE_ID = "uniqueId_1" 65 const val SENSOR_NAME = "test_sensor_name" 66 const val SENSOR_TYPE = "test_sensor_type" 67 } 68 69 @get:Rule 70 val rule = MockitoJUnit.rule()!! 71 72 private lateinit var context: Context 73 private lateinit var testLooper: TestLooper 74 private lateinit var ambientController: AmbientKeyboardBacklightController 75 76 @Mock 77 private lateinit var resources: Resources 78 79 @Mock 80 private lateinit var lightSensorInfo: InputSensorInfo 81 82 @Mock 83 private lateinit var sensorManager: SensorManager 84 85 @Mock 86 private lateinit var displayManagerInternal: DisplayManagerInternal 87 private lateinit var lightSensor: Sensor 88 89 private var currentDisplayInfo = DisplayInfo() 90 private var lastBrightnessCallback: Int = 0 91 private var listenerRegistered: Boolean = false 92 private var listenerRegistrationCount: Int = 0 93 94 @Before 95 fun setup() { 96 context = spy(ContextWrapper(ApplicationProvider.getApplicationContext())) 97 `when`(context.resources).thenReturn(resources) 98 setupBrightnessSteps() 99 setupSensor() 100 testLooper = TestLooper() 101 ambientController = AmbientKeyboardBacklightController(context, testLooper.looper) 102 ambientController.systemRunning() 103 testLooper.dispatchAll() 104 } 105 106 private fun setupBrightnessSteps() { 107 val brightnessValues = intArrayOf(100, 200, 0) 108 val decreaseThresholds = intArrayOf(-1, 900, 1900) 109 val increaseThresholds = intArrayOf(1000, 2000, -1) 110 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues)) 111 .thenReturn(brightnessValues) 112 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold)) 113 .thenReturn(decreaseThresholds) 114 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold)) 115 .thenReturn(increaseThresholds) 116 `when`( 117 resources.getValue( 118 eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant), 119 any(TypedValue::class.java), 120 anyBoolean() 121 ) 122 ).then { 123 val args = it.arguments 124 val outValue = args[1] as TypedValue 125 outValue.data = java.lang.Float.floatToRawIntBits(1.0f) 126 Unit 127 } 128 } 129 130 private fun setupSensor() { 131 LocalServices.removeServiceForTest(DisplayManagerInternal::class.java) 132 LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal) 133 currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID 134 `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn( 135 currentDisplayInfo 136 ) 137 val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE) 138 `when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY)) 139 .thenReturn(sensorData) 140 141 `when`(lightSensorInfo.name).thenReturn(SENSOR_NAME) 142 `when`(lightSensorInfo.stringType).thenReturn(SENSOR_TYPE) 143 lightSensor = Sensor(lightSensorInfo) 144 `when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager) 145 `when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor)) 146 `when`( 147 sensorManager.registerListener( 148 any(), 149 eq(lightSensor), 150 anyInt(), 151 any(Handler::class.java) 152 ) 153 ).then { 154 listenerRegistered = true 155 listenerRegistrationCount++ 156 true 157 } 158 `when`( 159 sensorManager.unregisterListener( 160 any(SensorEventListener::class.java), 161 eq(lightSensor) 162 ) 163 ).then { 164 listenerRegistered = false 165 Unit 166 } 167 } 168 169 private fun setupSensorWithInitialLux(luxValue: Float) { 170 ambientController.registerAmbientBacklightListener { brightnessValue: Int -> 171 lastBrightnessCallback = brightnessValue 172 } 173 sendAmbientLuxValue(luxValue) 174 testLooper.dispatchAll() 175 } 176 177 @Test 178 fun testInitialAmbientLux_sendsCallbackImmediately() { 179 setupSensorWithInitialLux(500F) 180 181 assertEquals( 182 "Should receive immediate callback for first lux change", 183 100, 184 lastBrightnessCallback 185 ) 186 } 187 188 @Test 189 fun testBrightnessIncrease_afterInitialLuxChanges() { 190 setupSensorWithInitialLux(500F) 191 192 // Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 193 repeat(HYSTERESIS_THRESHOLD) { 194 sendAmbientLuxValue(1500F) 195 } 196 testLooper.dispatchAll() 197 198 assertEquals( 199 "Should receive brightness change callback for increasing lux change", 200 200, 201 lastBrightnessCallback 202 ) 203 } 204 205 @Test 206 fun testBrightnessDecrease_afterInitialLuxChanges() { 207 setupSensorWithInitialLux(1500F) 208 209 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 210 repeat(HYSTERESIS_THRESHOLD) { 211 sendAmbientLuxValue(500F) 212 } 213 testLooper.dispatchAll() 214 215 assertEquals( 216 "Should receive brightness change callback for decreasing lux change", 217 100, 218 lastBrightnessCallback 219 ) 220 } 221 222 @Test 223 fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() { 224 val ambientListener = 225 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } 226 ambientController.registerAmbientBacklightListener(ambientListener) 227 228 assertThrows(IllegalStateException::class.java) { 229 ambientController.registerAmbientBacklightListener( 230 ambientListener 231 ) 232 } 233 } 234 235 @Test 236 fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() { 237 val ambientListener = 238 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } 239 assertThrows(IllegalStateException::class.java) { 240 ambientController.unregisterAmbientBacklightListener( 241 ambientListener 242 ) 243 } 244 } 245 246 @Test 247 fun testSensorListenerRegistered_onRegisterUnregisterAmbientListener() { 248 assertEquals( 249 "Should not have a sensor listener registered at init", 250 0, 251 listenerRegistrationCount 252 ) 253 assertFalse("Should not have a sensor listener registered at init", listenerRegistered) 254 255 val ambientListener1 = 256 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } 257 ambientController.registerAmbientBacklightListener(ambientListener1) 258 assertEquals( 259 "Should register a new sensor listener", 1, listenerRegistrationCount 260 ) 261 assertTrue("Should have sensor listener registered", listenerRegistered) 262 263 val ambientListener2 = 264 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } 265 ambientController.registerAmbientBacklightListener(ambientListener2) 266 assertEquals( 267 "Should not register a new sensor listener when adding a second ambient listener", 268 1, 269 listenerRegistrationCount 270 ) 271 assertTrue("Should have sensor listener registered", listenerRegistered) 272 273 ambientController.unregisterAmbientBacklightListener(ambientListener1) 274 assertTrue("Should have sensor listener registered", listenerRegistered) 275 276 ambientController.unregisterAmbientBacklightListener(ambientListener2) 277 assertFalse( 278 "Should not have sensor listener registered if there are no ambient listeners", 279 listenerRegistered 280 ) 281 } 282 283 @Test 284 fun testDisplayChange_shouldNotReRegisterListener_ifUniqueIdSame() { 285 setupSensorWithInitialLux(0F) 286 287 val count = listenerRegistrationCount 288 ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) 289 testLooper.dispatchAll() 290 291 assertEquals( 292 "Should not re-register listener on display change if unique is same", 293 count, 294 listenerRegistrationCount 295 ) 296 } 297 298 @Test 299 fun testDisplayChange_shouldReRegisterListener_ifUniqueIdChanges() { 300 setupSensorWithInitialLux(0F) 301 302 val count = listenerRegistrationCount 303 currentDisplayInfo.uniqueId = "xyz" 304 ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) 305 testLooper.dispatchAll() 306 307 assertEquals( 308 "Should re-register listener on display change if unique id changed", 309 count + 1, 310 listenerRegistrationCount 311 ) 312 } 313 314 @Test 315 fun testBrightnessDoesntChange_betweenIncreaseAndDecreaseThresholds() { 316 setupSensorWithInitialLux(1001F) 317 318 // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 319 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 320 lastBrightnessCallback = -1 321 repeat(HYSTERESIS_THRESHOLD) { 322 sendAmbientLuxValue(999F) 323 } 324 testLooper.dispatchAll() 325 326 assertEquals( 327 "Should not receive any callback for brightness change", 328 -1, 329 lastBrightnessCallback 330 ) 331 } 332 333 @Test 334 fun testBrightnessDoesntChange_onChangeOccurringLessThanHysteresisThreshold() { 335 setupSensorWithInitialLux(1001F) 336 337 // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 338 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 339 lastBrightnessCallback = -1 340 repeat(HYSTERESIS_THRESHOLD - 1) { 341 sendAmbientLuxValue(2001F) 342 } 343 testLooper.dispatchAll() 344 345 assertEquals( 346 "Should not receive any callback for brightness change", 347 -1, 348 lastBrightnessCallback 349 ) 350 } 351 352 private fun sendAmbientLuxValue(luxValue: Float) { 353 ambientController.onSensorChanged(SensorEvent(lightSensor, 0, 0, floatArrayOf(luxValue))) 354 } 355 } 356