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: atest InputTests:AmbientKeyboardBacklightControllerTests 58 */ 59 @Presubmit 60 class AmbientKeyboardBacklightControllerTests { 61 62 companion object { 63 const val DEFAULT_DISPLAY_UNIQUE_ID = "uniqueId_1" 64 const val SENSOR_NAME = "test_sensor_name" 65 const val SENSOR_TYPE = "test_sensor_type" 66 } 67 68 @get:Rule val rule = MockitoJUnit.rule()!! 69 70 private lateinit var context: Context 71 private lateinit var testLooper: TestLooper 72 private lateinit var ambientController: AmbientKeyboardBacklightController 73 74 @Mock private lateinit var resources: Resources 75 76 @Mock private lateinit var lightSensorInfo: InputSensorInfo 77 78 @Mock private lateinit var sensorManager: SensorManager 79 80 @Mock private lateinit var displayManagerInternal: DisplayManagerInternal 81 private lateinit var lightSensor: Sensor 82 83 private var currentDisplayInfo = DisplayInfo() 84 private var lastBrightnessCallback: Int = 0 85 private var listenerRegistered: Boolean = false 86 private var listenerRegistrationCount: Int = 0 87 88 @Before 89 fun setup() { 90 context = spy(ContextWrapper(ApplicationProvider.getApplicationContext())) 91 `when`(context.resources).thenReturn(resources) 92 setupBrightnessSteps() 93 setupSensor() 94 testLooper = TestLooper() 95 ambientController = AmbientKeyboardBacklightController(context, testLooper.looper) 96 ambientController.systemRunning() 97 testLooper.dispatchAll() 98 } 99 100 private fun setupBrightnessSteps() { 101 val brightnessValues = intArrayOf(100, 200, 0) 102 val decreaseThresholds = intArrayOf(-1, 900, 1900) 103 val increaseThresholds = intArrayOf(1000, 2000, -1) 104 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues)) 105 .thenReturn(brightnessValues) 106 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold)) 107 .thenReturn(decreaseThresholds) 108 `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold)) 109 .thenReturn(increaseThresholds) 110 `when`( 111 resources.getValue( 112 eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant), 113 any(TypedValue::class.java), 114 anyBoolean(), 115 ) 116 ) 117 .then { 118 val args = it.arguments 119 val outValue = args[1] as TypedValue 120 outValue.data = java.lang.Float.floatToRawIntBits(1.0f) 121 Unit 122 } 123 } 124 125 private fun setupSensor() { 126 LocalServices.removeServiceForTest(DisplayManagerInternal::class.java) 127 LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal) 128 currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID 129 `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)) 130 .thenReturn(currentDisplayInfo) 131 val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE) 132 `when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY)) 133 .thenReturn(sensorData) 134 135 `when`(lightSensorInfo.name).thenReturn(SENSOR_NAME) 136 `when`(lightSensorInfo.stringType).thenReturn(SENSOR_TYPE) 137 lightSensor = Sensor(lightSensorInfo) 138 `when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager) 139 `when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor)) 140 `when`( 141 sensorManager.registerListener( 142 any(), 143 eq(lightSensor), 144 anyInt(), 145 any(Handler::class.java), 146 ) 147 ) 148 .then { 149 listenerRegistered = true 150 listenerRegistrationCount++ 151 true 152 } 153 `when`( 154 sensorManager.unregisterListener( 155 any(SensorEventListener::class.java), 156 eq(lightSensor), 157 ) 158 ) 159 .then { 160 listenerRegistered = false 161 Unit 162 } 163 } 164 165 private fun setupSensorWithInitialLux(luxValue: Float) { 166 ambientController.registerAmbientBacklightListener { brightnessValue: Int -> 167 lastBrightnessCallback = brightnessValue 168 } 169 sendAmbientLuxValue(luxValue) 170 testLooper.dispatchAll() 171 } 172 173 @Test 174 fun testInitialAmbientLux_sendsCallbackImmediately() { 175 setupSensorWithInitialLux(500F) 176 177 assertEquals( 178 "Should receive immediate callback for first lux change", 179 100, 180 lastBrightnessCallback, 181 ) 182 } 183 184 @Test 185 fun testBrightnessIncrease_afterInitialLuxChanges() { 186 setupSensorWithInitialLux(500F) 187 188 // Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 189 repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(1500F) } 190 testLooper.dispatchAll() 191 192 assertEquals( 193 "Should receive brightness change callback for increasing lux change", 194 200, 195 lastBrightnessCallback, 196 ) 197 } 198 199 @Test 200 fun testBrightnessDecrease_afterInitialLuxChanges() { 201 setupSensorWithInitialLux(1500F) 202 203 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 204 repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(500F) } 205 testLooper.dispatchAll() 206 207 assertEquals( 208 "Should receive brightness change callback for decreasing lux change", 209 100, 210 lastBrightnessCallback, 211 ) 212 } 213 214 @Test 215 fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() { 216 val ambientListener = AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {} 217 ambientController.registerAmbientBacklightListener(ambientListener) 218 219 assertThrows(IllegalStateException::class.java) { 220 ambientController.registerAmbientBacklightListener(ambientListener) 221 } 222 } 223 224 @Test 225 fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() { 226 val ambientListener = AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {} 227 assertThrows(IllegalStateException::class.java) { 228 ambientController.unregisterAmbientBacklightListener(ambientListener) 229 } 230 } 231 232 @Test 233 fun testSensorListenerRegistered_onRegisterUnregisterAmbientListener() { 234 assertEquals( 235 "Should not have a sensor listener registered at init", 236 0, 237 listenerRegistrationCount, 238 ) 239 assertFalse("Should not have a sensor listener registered at init", listenerRegistered) 240 241 val ambientListener1 = 242 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {} 243 ambientController.registerAmbientBacklightListener(ambientListener1) 244 assertEquals("Should register a new sensor listener", 1, listenerRegistrationCount) 245 assertTrue("Should have sensor listener registered", listenerRegistered) 246 247 val ambientListener2 = 248 AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener {} 249 ambientController.registerAmbientBacklightListener(ambientListener2) 250 assertEquals( 251 "Should not register a new sensor listener when adding a second ambient listener", 252 1, 253 listenerRegistrationCount, 254 ) 255 assertTrue("Should have sensor listener registered", listenerRegistered) 256 257 ambientController.unregisterAmbientBacklightListener(ambientListener1) 258 assertTrue("Should have sensor listener registered", listenerRegistered) 259 260 ambientController.unregisterAmbientBacklightListener(ambientListener2) 261 assertFalse( 262 "Should not have sensor listener registered if there are no ambient listeners", 263 listenerRegistered, 264 ) 265 } 266 267 @Test 268 fun testDisplayChange_shouldNotReRegisterListener_ifUniqueIdSame() { 269 setupSensorWithInitialLux(0F) 270 271 val count = listenerRegistrationCount 272 ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) 273 testLooper.dispatchAll() 274 275 assertEquals( 276 "Should not re-register listener on display change if unique is same", 277 count, 278 listenerRegistrationCount, 279 ) 280 } 281 282 @Test 283 fun testDisplayChange_shouldReRegisterListener_ifUniqueIdChanges() { 284 setupSensorWithInitialLux(0F) 285 286 val count = listenerRegistrationCount 287 currentDisplayInfo.uniqueId = "xyz" 288 ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) 289 testLooper.dispatchAll() 290 291 assertEquals( 292 "Should re-register listener on display change if unique id changed", 293 count + 1, 294 listenerRegistrationCount, 295 ) 296 } 297 298 @Test 299 fun testBrightnessDoesntChange_betweenIncreaseAndDecreaseThresholds() { 300 setupSensorWithInitialLux(1001F) 301 302 // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 303 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 304 lastBrightnessCallback = -1 305 repeat(HYSTERESIS_THRESHOLD) { sendAmbientLuxValue(999F) } 306 testLooper.dispatchAll() 307 308 assertEquals( 309 "Should not receive any callback for brightness change", 310 -1, 311 lastBrightnessCallback, 312 ) 313 } 314 315 @Test 316 fun testBrightnessDoesntChange_onChangeOccurringLessThanHysteresisThreshold() { 317 setupSensorWithInitialLux(1001F) 318 319 // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] 320 // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] 321 lastBrightnessCallback = -1 322 repeat(HYSTERESIS_THRESHOLD - 1) { sendAmbientLuxValue(2001F) } 323 testLooper.dispatchAll() 324 325 assertEquals( 326 "Should not receive any callback for brightness change", 327 -1, 328 lastBrightnessCallback, 329 ) 330 } 331 332 private fun sendAmbientLuxValue(luxValue: Float) { 333 ambientController.onSensorChanged(SensorEvent(lightSensor, 0, 0, floatArrayOf(luxValue))) 334 } 335 } 336