1 /*
2  * Copyright (C) 2021 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 android.hardware.input;
18 
19 import static android.hardware.lights.LightsRequest.Builder;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static junit.framework.TestCase.assertEquals;
24 import static junit.framework.TestCase.assertNotNull;
25 
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.doAnswer;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import android.content.Context;
36 import android.content.ContextWrapper;
37 import android.hardware.lights.Light;
38 import android.hardware.lights.LightState;
39 import android.hardware.lights.LightsManager;
40 import android.hardware.lights.LightsRequest;
41 import android.os.IBinder;
42 import android.platform.test.annotations.Presubmit;
43 import android.util.ArrayMap;
44 import android.view.InputDevice;
45 
46 import androidx.test.InstrumentationRegistry;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.Mock;
54 import org.mockito.junit.MockitoJUnit;
55 import org.mockito.junit.MockitoJUnitRunner;
56 import org.mockito.junit.MockitoRule;
57 
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.List;
61 
62 /**
63  * Tests for {@link InputDeviceLightsManager}.
64  *
65  * Build/Install/Run:
66  * atest FrameworksCoreTests:InputDeviceLightsManagerTest
67  */
68 @Presubmit
69 @RunWith(MockitoJUnitRunner.class)
70 public class InputDeviceLightsManagerTest {
71     private static final String TAG = "InputDeviceLightsManagerTest";
72 
73     private static final int DEVICE_ID = 1000;
74     private static final int PLAYER_ID = 3;
75 
76     @Rule public final MockitoRule mockito = MockitoJUnit.rule();
77 
78     private InputManager mInputManager;
79 
80     @Mock private IInputManager mIInputManagerMock;
81 
82     @Before
setUp()83     public void setUp() throws Exception {
84         final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
85         when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
86 
87         when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
88                 createInputDevice(DEVICE_ID));
89 
90         InputManagerGlobal.resetInstance(mIInputManagerMock);
91         mInputManager = new InputManager(context);
92         when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
93 
94         ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>();
95         doAnswer(invocation -> {
96             final int[] lightIds = (int[]) invocation.getArguments()[1];
97             final LightState[] lightStates =
98                     (LightState[]) invocation.getArguments()[2];
99             for (int i = 0; i < lightIds.length; i++) {
100                 lightStatesById.put(lightIds[i], lightStates[i]);
101             }
102             return null;
103         }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
104                 any(int[].class), any(LightState[].class), any(IBinder.class));
105 
106         doAnswer(invocation -> {
107             int lightId = (int) invocation.getArguments()[1];
108             if (lightStatesById.containsKey(lightId)) {
109                 return lightStatesById.get(lightId);
110             }
111             return new LightState(0);
112         }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
113     }
114 
115     @After
tearDown()116     public void tearDown() {
117         InputManagerGlobal.clearInstance();
118     }
119 
createInputDevice(int id)120     private InputDevice createInputDevice(int id) {
121         return new InputDevice.Builder()
122                 .setId(id)
123                 .setName("Test Device " + id)
124                 .build();
125     }
126 
mockLights(Light[] lights)127     private void mockLights(Light[] lights) throws Exception {
128         // Mock the Lights returned form InputManagerService
129         when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
130                 new ArrayList(Arrays.asList(lights)));
131     }
132 
133     @Test
testGetInputDeviceLights()134     public void testGetInputDeviceLights() throws Exception {
135         InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
136         assertNotNull(device);
137 
138         Light[] mockedLights = {
139             new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
140                         Light.LIGHT_CAPABILITY_BRIGHTNESS),
141             new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
142                         Light.LIGHT_CAPABILITY_COLOR_RGB),
143             new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
144                         0 /* capabilities */)
145         };
146         mockLights(mockedLights);
147 
148         LightsManager lightsManager = device.getLightsManager();
149         List<Light> lights = lightsManager.getLights();
150         verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
151         assertEquals(lights, Arrays.asList(mockedLights));
152     }
153 
154     @Test
testControlMultipleLights()155     public void testControlMultipleLights() throws Exception {
156         InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
157         assertNotNull(device);
158 
159         Light[] mockedLights = {
160             new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
161                         Light.LIGHT_CAPABILITY_COLOR_RGB),
162             new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
163                         Light.LIGHT_CAPABILITY_COLOR_RGB),
164             new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
165                         Light.LIGHT_CAPABILITY_COLOR_RGB),
166             new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
167                         Light.LIGHT_CAPABILITY_COLOR_RGB)
168         };
169         mockLights(mockedLights);
170 
171         LightsManager lightsManager = device.getLightsManager();
172         List<Light> lightList = lightsManager.getLights();
173         LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2),
174                 new LightState(0xf3)};
175         // Open a session to request turn 3/4 lights on:
176         LightsManager.LightsSession session = lightsManager.openSession();
177         session.requestLights(new Builder()
178                 .addLight(lightsManager.getLights().get(0), states[0])
179                 .addLight(lightsManager.getLights().get(1), states[1])
180                 .addLight(lightsManager.getLights().get(2), states[2])
181                 .build());
182         IBinder token = session.getToken();
183 
184         verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
185                 any(String.class), eq(token));
186         verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
187                 eq(states), eq(token));
188 
189         // Then all 3 should turn on.
190         assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
191                 .isEqualTo(0xf1);
192         assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor())
193                 .isEqualTo(0xf2);
194         assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor())
195                 .isEqualTo(0xf3);
196 
197         // And the 4th should remain off.
198         assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor())
199                 .isEqualTo(0x00);
200 
201         // close session
202         session.close();
203         verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
204     }
205 
206     @Test
testControlPlayerIdLight()207     public void testControlPlayerIdLight() throws Exception {
208         InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
209         assertNotNull(device);
210 
211         Light[] mockedLights = {
212                 new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
213                             0 /* capabilities */),
214                 new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
215                             Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
216                 new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
217                             Light.LIGHT_CAPABILITY_BRIGHTNESS)
218         };
219         mockLights(mockedLights);
220 
221         LightsManager lightsManager = device.getLightsManager();
222         List<Light> lightList = lightsManager.getLights();
223         LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)};
224         // Open a session to request set Player ID light:
225         LightsManager.LightsSession session = lightsManager.openSession();
226         session.requestLights(new Builder()
227                 .addLight(lightsManager.getLights().get(0), states[0])
228                 .build());
229         IBinder token = session.getToken();
230 
231         verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
232                 any(String.class), eq(token));
233         verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
234                 eq(states), eq(token));
235 
236         // Verify the light state
237         assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
238                 .isEqualTo(0xf1);
239         assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId())
240                 .isEqualTo(PLAYER_ID);
241 
242         // close session
243         session.close();
244         verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
245     }
246 
247     @Test
testLightCapabilities()248     public void testLightCapabilities() throws Exception {
249         Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
250                 Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
251         assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
252         assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
253                 | Light.LIGHT_CAPABILITY_BRIGHTNESS);
254         assertTrue(light.hasBrightnessControl());
255         assertTrue(light.hasRgbControl());
256     }
257 
258     @Test
testLightsRequest()259     public void testLightsRequest() throws Exception {
260         Light light1 = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
261                 0 /* capabilities */);
262         Light light2 = new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
263                 0 /* capabilities */);
264         LightState state1 = new LightState(0xf1);
265         LightState state2 = new LightState(0xf2, PLAYER_ID);
266         LightsRequest request = new Builder().addLight(light1, state1)
267                 .addLight(light2, state2).build();
268 
269         // Covers the LightsRequest.getLights
270         assertThat(request.getLights().size()).isEqualTo(2);
271         assertThat(request.getLights().get(0)).isEqualTo(1);
272         assertThat(request.getLights().get(1)).isEqualTo(2);
273 
274         // Covers the LightsRequest.getLightStates
275         assertThat(request.getLightStates().size()).isEqualTo(2);
276         assertThat(request.getLightStates().get(0)).isEqualTo(state1);
277         assertThat(request.getLightStates().get(1)).isEqualTo(state2);
278 
279         // Covers the LightsRequest.getLightsAndStates
280         assertThat(request.getLightsAndStates().size()).isEqualTo(2);
281         assertThat(request.getLightsAndStates().containsKey(light1)).isTrue();
282         assertThat(request.getLightsAndStates().get(light1)).isEqualTo(state1);
283         assertThat(request.getLightsAndStates().get(light2)).isEqualTo(state2);
284     }
285 
286 }
287