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