• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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