1 /*
2  * Copyright (C) 2020 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.policy;
18 
19 
20 import static android.content.Context.SENSOR_SERVICE;
21 
22 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
23 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
24 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
25 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
26 import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
27 import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
28 
29 import static org.junit.Assert.assertArrayEquals;
30 import static org.junit.Assert.assertEquals;
31 import static org.mockito.ArgumentMatchers.anyInt;
32 import static org.mockito.Mockito.eq;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.annotation.Nullable;
39 import android.content.Context;
40 import android.hardware.Sensor;
41 import android.hardware.SensorEvent;
42 import android.hardware.SensorManager;
43 import android.os.PowerManager;
44 
45 import androidx.annotation.NonNull;
46 
47 import com.android.server.LocalServices;
48 import com.android.server.devicestate.DeviceState;
49 import com.android.server.devicestate.DeviceStateProvider;
50 import com.android.server.input.InputManagerInternal;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 import org.mockito.ArgumentCaptor;
56 import org.mockito.Mockito;
57 import org.mockito.internal.util.reflection.FieldSetter;
58 
59 import java.io.ByteArrayInputStream;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.lang.reflect.Constructor;
63 import java.util.List;
64 
65 /**
66  * Unit tests for {@link DeviceStateProviderImpl}.
67  * <p/>
68  * Run with <code>atest DeviceStateProviderImplTest</code>.
69  */
70 public final class DeviceStateProviderImplTest {
71     private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
72             DeviceState[].class);
73     private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
74     private static final int MAX_HINGE_ANGLE_EXCLUSIVE = 360;
75 
76     private Context mContext;
77     private SensorManager mSensorManager;
78 
79     @Before
setup()80     public void setup() {
81         LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
82         mContext = mock(Context.class);
83         mSensorManager = mock(SensorManager.class);
84         when(mContext.getSystemServiceName(eq(SensorManager.class))).thenReturn(SENSOR_SERVICE);
85         when(mContext.getSystemService(eq(SENSOR_SERVICE))).thenReturn(mSensorManager);
86     }
87 
88     @After
tearDown()89     public void tearDown() {
90         LocalServices.removeServiceForTest(InputManagerInternal.class);
91     }
92 
93     @Test
create_noConfig()94     public void create_noConfig() {
95         assertDefaultProviderValues(null);
96     }
97 
98     @Test
create_emptyFile()99     public void create_emptyFile() {
100         String configString = "";
101         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
102 
103         assertDefaultProviderValues(config);
104     }
105 
106     @Test
create_emptyConfig()107     public void create_emptyConfig() {
108         String configString = "<device-state-config></device-state-config>";
109         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
110 
111         assertDefaultProviderValues(config);
112     }
113 
114     @Test
create_invalidConfig()115     public void create_invalidConfig() {
116         String configString = "<device-state-config>\n"
117                 + "    </device-state>\n"
118                 + "</device-state-config>\n";
119         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
120 
121         assertDefaultProviderValues(config);
122     }
123 
assertDefaultProviderValues( @ullable DeviceStateProviderImpl.ReadableConfig config)124     private void assertDefaultProviderValues(
125             @Nullable DeviceStateProviderImpl.ReadableConfig config) {
126         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
127                 config);
128 
129         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
130         provider.setListener(listener);
131 
132         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
133                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
134         assertArrayEquals(new DeviceState[]{DEFAULT_DEVICE_STATE},
135                 mDeviceStateArrayCaptor.getValue());
136 
137         verify(listener).onStateChanged(mIntegerCaptor.capture());
138         assertEquals(DEFAULT_DEVICE_STATE.getIdentifier(), mIntegerCaptor.getValue().intValue());
139     }
140 
141     @Test
create_multipleMatchingStatesDefaultsToLowestIdentifier()142     public void create_multipleMatchingStatesDefaultsToLowestIdentifier() {
143         String configString = "<device-state-config>\n"
144                 + "    <device-state>\n"
145                 + "        <identifier>1</identifier>\n"
146                 + "        <conditions/>\n"
147                 + "    </device-state>\n"
148                 + "    <device-state>\n"
149                 + "        <identifier>2</identifier>\n"
150                 + "        <conditions/>\n"
151                 + "    </device-state>\n"
152                 + "</device-state-config>\n";
153         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
154         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
155                 config);
156 
157         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
158         provider.setListener(listener);
159 
160         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
161                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
162         final DeviceState[] expectedStates = new DeviceState[]{
163                 new DeviceState(1, "", 0 /* flags */),
164                 new DeviceState(2, "", 0 /* flags */) };
165         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
166 
167         verify(listener).onStateChanged(mIntegerCaptor.capture());
168         assertEquals(1, mIntegerCaptor.getValue().intValue());
169     }
170 
171     @Test
create_stateWithCancelOverrideRequestFlag()172     public void create_stateWithCancelOverrideRequestFlag() {
173         String configString = "<device-state-config>\n"
174                 + "    <device-state>\n"
175                 + "        <identifier>1</identifier>\n"
176                 + "        <flags>\n"
177                 + "            <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
178                 + "        </flags>\n"
179                 + "        <conditions/>\n"
180                 + "    </device-state>\n"
181                 + "    <device-state>\n"
182                 + "        <identifier>2</identifier>\n"
183                 + "        <conditions/>\n"
184                 + "    </device-state>\n"
185                 + "</device-state-config>\n";
186         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
187         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
188                 config);
189 
190         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
191         provider.setListener(listener);
192 
193         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
194                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
195         final DeviceState[] expectedStates = new DeviceState[]{
196                 new DeviceState(1, "", DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS),
197                 new DeviceState(2, "", 0 /* flags */) };
198         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
199     }
200 
201     @Test
create_stateWithInvalidFlag()202     public void create_stateWithInvalidFlag() {
203         String configString = "<device-state-config>\n"
204                 + "    <device-state>\n"
205                 + "        <identifier>1</identifier>\n"
206                 + "        <flags>\n"
207                 + "            <flag>INVALID_FLAG</flag>\n"
208                 + "        </flags>\n"
209                 + "        <conditions/>\n"
210                 + "    </device-state>\n"
211                 + "    <device-state>\n"
212                 + "        <identifier>2</identifier>\n"
213                 + "        <conditions/>\n"
214                 + "    </device-state>\n"
215                 + "</device-state-config>\n";
216         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
217         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
218                 config);
219 
220         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
221         provider.setListener(listener);
222 
223         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
224                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
225         final DeviceState[] expectedStates = new DeviceState[]{
226                 new DeviceState(1, "", 0 /* flags */),
227                 new DeviceState(2, "", 0 /* flags */) };
228         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
229     }
230 
231     @Test
create_lidSwitch()232     public void create_lidSwitch() {
233         String configString = "<device-state-config>\n"
234                 + "    <device-state>\n"
235                 + "        <identifier>1</identifier>\n"
236                 + "        <conditions>\n"
237                 + "            <lid-switch>\n"
238                 + "                <open>true</open>\n"
239                 + "            </lid-switch>\n"
240                 + "        </conditions>\n"
241                 + "    </device-state>\n"
242                 + "    <device-state>\n"
243                 + "        <identifier>2</identifier>\n"
244                 + "        <name>CLOSED</name>\n"
245                 + "        <conditions>\n"
246                 + "            <lid-switch>\n"
247                 + "                <open>false</open>\n"
248                 + "            </lid-switch>\n"
249                 + "        </conditions>\n"
250                 + "    </device-state>\n"
251                 + "</device-state-config>\n";
252         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
253         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
254                 config);
255 
256         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
257         provider.setListener(listener);
258 
259         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
260                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
261         final DeviceState[] expectedStates = new DeviceState[]{
262                 new DeviceState(1, "", 0 /* flags */),
263                 new DeviceState(2, "CLOSED", 0 /* flags */) };
264         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
265 
266         // onStateChanged() should not be called because the provider has not yet been notified of
267         // the initial lid switch state.
268         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
269 
270         provider.notifyLidSwitchChanged(0, false /* lidOpen */);
271 
272         verify(listener).onStateChanged(mIntegerCaptor.capture());
273         assertEquals(2, mIntegerCaptor.getValue().intValue());
274 
275         Mockito.clearInvocations(listener);
276 
277         provider.notifyLidSwitchChanged(1, true /* lidOpen */);
278         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
279                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
280         verify(listener).onStateChanged(mIntegerCaptor.capture());
281         assertEquals(1, mIntegerCaptor.getValue().intValue());
282     }
283 
create_sensorBasedProvider(Sensor sensor)284     private DeviceStateProviderImpl create_sensorBasedProvider(Sensor sensor) {
285         String configString = "<device-state-config>\n"
286                 + "    <device-state>\n"
287                 + "        <identifier>1</identifier>\n"
288                 + "        <name>CLOSED</name>\n"
289                 + "        <conditions>\n"
290                 + "            <sensor>\n"
291                 + "                <type>" + sensor.getStringType() + "</type>\n"
292                 + "                <name>" + sensor.getName() + "</name>\n"
293                 + "                <value>\n"
294                 + "                    <max>90</max>\n"
295                 + "                </value>\n"
296                 + "            </sensor>\n"
297                 + "        </conditions>\n"
298                 + "    </device-state>\n"
299                 + "    <device-state>\n"
300                 + "        <identifier>2</identifier>\n"
301                 + "        <name>HALF_OPENED</name>\n"
302                 + "        <conditions>\n"
303                 + "            <sensor>\n"
304                 + "                <type>" + sensor.getStringType() + "</type>\n"
305                 + "                <name>" + sensor.getName() + "</name>\n"
306                 + "                <value>\n"
307                 + "                    <min-inclusive>90</min-inclusive>\n"
308                 + "                    <max>180</max>\n"
309                 + "                </value>\n"
310                 + "            </sensor>\n"
311                 + "        </conditions>\n"
312                 + "    </device-state>\n"
313                 + "    <device-state>\n"
314                 + "        <identifier>3</identifier>\n"
315                 + "        <name>OPENED</name>\n"
316                 + "        <conditions>\n"
317                 + "            <sensor>\n"
318                 + "                <type>" + sensor.getStringType() + "</type>\n"
319                 + "                <name>" + sensor.getName() + "</name>\n"
320                 + "                <value>\n"
321                 + "                    <min-inclusive>180</min-inclusive>\n"
322                 + "                    <max>" + MAX_HINGE_ANGLE_EXCLUSIVE + "</max>\n"
323                 + "                </value>\n"
324                 + "            </sensor>\n"
325                 + "        </conditions>\n"
326                 + "    </device-state>\n"
327                 + "    <device-state>\n"
328                 + "        <identifier>4</identifier>\n"
329                 + "        <name>THERMAL_TEST</name>\n"
330                 + "        <flags>\n"
331                 + "            <flag>FLAG_EMULATED_ONLY</flag>\n"
332                 + "            <flag>FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL</flag>\n"
333                 + "            <flag>FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE</flag>\n"
334                 + "        </flags>\n"
335                 + "    </device-state>\n"
336                 + "</device-state-config>\n";
337         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
338         return DeviceStateProviderImpl.createFromConfig(mContext,
339                 config);
340     }
341 
342     @Test
create_sensor()343     public void create_sensor() throws Exception {
344         Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
345         when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
346         DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
347 
348         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
349         provider.setListener(listener);
350 
351         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
352                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
353         assertArrayEquals(
354                 new DeviceState[]{
355                         new DeviceState(1, "CLOSED", 0 /* flags */),
356                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
357                         new DeviceState(3, "OPENED", 0 /* flags */),
358                         new DeviceState(4, "THERMAL_TEST",
359                                 DeviceState.FLAG_EMULATED_ONLY
360                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
361                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
362                 mDeviceStateArrayCaptor.getValue());
363         // onStateChanged() should not be called because the provider has not yet been notified of
364         // the initial sensor state.
365         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
366 
367         Mockito.clearInvocations(listener);
368 
369         SensorEvent event0 = mock(SensorEvent.class);
370         event0.sensor = sensor;
371         FieldSetter.setField(event0, event0.getClass().getField("values"), new float[]{180});
372 
373         provider.onSensorChanged(event0);
374 
375         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
376                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
377         verify(listener).onStateChanged(mIntegerCaptor.capture());
378         assertEquals(3, mIntegerCaptor.getValue().intValue());
379 
380         Mockito.clearInvocations(listener);
381 
382         SensorEvent event1 = mock(SensorEvent.class);
383         event1.sensor = sensor;
384         FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
385 
386         provider.onSensorChanged(event1);
387 
388         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
389                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
390         verify(listener).onStateChanged(mIntegerCaptor.capture());
391         assertEquals(2, mIntegerCaptor.getValue().intValue());
392 
393         Mockito.clearInvocations(listener);
394 
395         SensorEvent event2 = mock(SensorEvent.class);
396         event2.sensor = sensor;
397         FieldSetter.setField(event2, event2.getClass().getField("values"), new float[]{0});
398 
399         provider.onSensorChanged(event2);
400 
401         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
402                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
403         verify(listener).onStateChanged(mIntegerCaptor.capture());
404         assertEquals(1, mIntegerCaptor.getValue().intValue());
405     }
406 
407     @Test
test_flagDisableWhenThermalStatusCritical()408     public void test_flagDisableWhenThermalStatusCritical() throws Exception {
409         Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
410         when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
411         DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
412 
413         provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
414         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
415         provider.setListener(listener);
416 
417         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
418                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
419         assertArrayEquals(
420                 new DeviceState[]{
421                         new DeviceState(1, "CLOSED", 0 /* flags */),
422                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
423                         new DeviceState(3, "OPENED", 0 /* flags */),
424                         new DeviceState(4, "THERMAL_TEST",
425                                 DeviceState.FLAG_EMULATED_ONLY
426                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
427                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
428                 mDeviceStateArrayCaptor.getValue());
429         Mockito.clearInvocations(listener);
430 
431         provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE);
432         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
433                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
434         Mockito.clearInvocations(listener);
435 
436         // The THERMAL_TEST state should be disabled.
437         provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL);
438         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
439                 eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL));
440         assertArrayEquals(
441                 new DeviceState[]{
442                         new DeviceState(1, "CLOSED", 0 /* flags */),
443                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
444                         new DeviceState(3, "OPENED", 0 /* flags */) },
445                 mDeviceStateArrayCaptor.getValue());
446         Mockito.clearInvocations(listener);
447 
448         // The THERMAL_TEST state should be re-enabled.
449         provider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
450         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
451                 eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL));
452         assertArrayEquals(
453                 new DeviceState[]{
454                         new DeviceState(1, "CLOSED", 0 /* flags */),
455                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
456                         new DeviceState(3, "OPENED", 0 /* flags */),
457                         new DeviceState(4, "THERMAL_TEST",
458                                 DeviceState.FLAG_EMULATED_ONLY
459                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
460                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
461                 mDeviceStateArrayCaptor.getValue());
462     }
463 
464     @Test
test_flagDisableWhenPowerSaveEnabled()465     public void test_flagDisableWhenPowerSaveEnabled() throws Exception {
466         Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
467         when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
468         DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
469 
470         provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
471         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
472         provider.setListener(listener);
473 
474         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
475                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
476         assertArrayEquals(
477                 new DeviceState[]{
478                         new DeviceState(1, "CLOSED", 0 /* flags */),
479                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
480                         new DeviceState(3, "OPENED", 0 /* flags */),
481                         new DeviceState(4, "THERMAL_TEST",
482                                 DeviceState.FLAG_EMULATED_ONLY
483                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
484                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
485                 mDeviceStateArrayCaptor.getValue());
486         Mockito.clearInvocations(listener);
487 
488         provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
489         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
490                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
491         Mockito.clearInvocations(listener);
492 
493         // The THERMAL_TEST state should be disabled due to power save being enabled.
494         provider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */);
495         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
496                 eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED));
497         assertArrayEquals(
498                 new DeviceState[]{
499                         new DeviceState(1, "CLOSED", 0 /* flags */),
500                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
501                         new DeviceState(3, "OPENED", 0 /* flags */) },
502                 mDeviceStateArrayCaptor.getValue());
503         Mockito.clearInvocations(listener);
504 
505         // The THERMAL_TEST state should be re-enabled.
506         provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
507         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
508                 eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED));
509         assertArrayEquals(
510                 new DeviceState[]{
511                         new DeviceState(1, "CLOSED", 0 /* flags */),
512                         new DeviceState(2, "HALF_OPENED", 0 /* flags */),
513                         new DeviceState(3, "OPENED", 0 /* flags */),
514                         new DeviceState(4, "THERMAL_TEST",
515                                 DeviceState.FLAG_EMULATED_ONLY
516                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
517                                         | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
518                 mDeviceStateArrayCaptor.getValue());
519     }
520 
521     @Test
test_invalidSensorValues()522     public void test_invalidSensorValues() throws Exception {
523         // onStateChanged() should not be triggered by invalid sensor values.
524 
525         Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
526         when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
527         DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
528 
529         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
530         provider.setListener(listener);
531         Mockito.clearInvocations(listener);
532 
533         // First, switch to a non-default state.
534         SensorEvent event1 = mock(SensorEvent.class);
535         event1.sensor = sensor;
536         FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
537         provider.onSensorChanged(event1);
538         verify(listener).onStateChanged(mIntegerCaptor.capture());
539         assertEquals(2, mIntegerCaptor.getValue().intValue());
540 
541         Mockito.clearInvocations(listener);
542 
543         // Then, send an invalid sensor event, verify that onStateChanged() is not triggered.
544         SensorEvent event2 = mock(SensorEvent.class);
545         event2.sensor = sensor;
546         FieldSetter.setField(event2, event2.getClass().getField("values"),
547                 new float[]{MAX_HINGE_ANGLE_EXCLUSIVE});
548 
549         provider.onSensorChanged(event2);
550 
551         verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
552                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
553         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
554     }
555 
556     @Test
create_invalidSensor()557     public void create_invalidSensor() throws Exception {
558         Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
559         when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of());
560 
561         String configString = "<device-state-config>\n"
562                 + "    <device-state>\n"
563                 + "        <identifier>1</identifier>\n"
564                 + "        <name>CLOSED</name>\n"
565                 + "        <conditions>\n"
566                 + "            <sensor>\n"
567                 + "                <type>" + sensor.getStringType() + "</type>\n"
568                 + "                <name>" + sensor.getName() + "</name>\n"
569                 + "                <value>\n"
570                 + "                    <max>90</max>\n"
571                 + "                </value>\n"
572                 + "            </sensor>\n"
573                 + "        </conditions>\n"
574                 + "    </device-state>\n"
575                 + "    <device-state>\n"
576                 + "        <identifier>2</identifier>\n"
577                 + "        <name>HALF_OPENED</name>\n"
578                 + "        <conditions>\n"
579                 + "            <sensor>\n"
580                 + "                <type>" + sensor.getStringType() + "</type>\n"
581                 + "                <name>" + sensor.getName() + "</name>\n"
582                 + "                <value>\n"
583                 + "                    <min-inclusive>90</min-inclusive>\n"
584                 + "                    <max>180</max>\n"
585                 + "                </value>\n"
586                 + "            </sensor>\n"
587                 + "        </conditions>\n"
588                 + "    </device-state>\n"
589                 + "</device-state-config>\n";
590         DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
591         DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
592                 config);
593 
594         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
595         provider.setListener(listener);
596 
597         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
598                 eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
599         assertArrayEquals(
600                 new DeviceState[]{
601                         new DeviceState(1, "CLOSED", 0 /* flags */),
602                         new DeviceState(2, "HALF_OPENED", 0 /* flags */)
603                 }, mDeviceStateArrayCaptor.getValue());
604         // onStateChanged() should not be called because the provider could not find the sensor.
605         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
606     }
607 
newSensor(String name, String type)608     private static Sensor newSensor(String name, String type) throws Exception {
609         Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor();
610         constructor.setAccessible(true);
611 
612         Sensor sensor = constructor.newInstance();
613         FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name);
614         FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mStringType"), type);
615         return sensor;
616     }
617 
618     private static final class TestReadableConfig implements
619             DeviceStateProviderImpl.ReadableConfig {
620         private final byte[] mData;
621 
TestReadableConfig(String configFileData)622         TestReadableConfig(String configFileData) {
623             mData = configFileData.getBytes();
624         }
625 
626         @NonNull
627         @Override
openRead()628         public InputStream openRead() throws IOException {
629             return new ByteArrayInputStream(mData);
630         }
631     }
632 }
633