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.lights;
18 
19 import static android.graphics.Color.BLUE;
20 import static android.graphics.Color.GREEN;
21 import static android.graphics.Color.TRANSPARENT;
22 import static android.graphics.Color.WHITE;
23 import static android.hardware.lights.LightsRequest.Builder;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import android.content.Context;
28 import android.hardware.light.HwLight;
29 import android.hardware.light.HwLightState;
30 import android.hardware.light.ILights;
31 import android.hardware.lights.Light;
32 import android.hardware.lights.LightState;
33 import android.hardware.lights.LightsManager;
34 import android.hardware.lights.SystemLightsManager;
35 import android.os.Looper;
36 
37 import androidx.test.filters.SmallTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.Mock;
44 import org.mockito.MockitoAnnotations;
45 
46 @RunWith(AndroidJUnit4.class)
47 @SmallTest
48 public class LightsServiceTest {
49 
50     private static final int HIGH_PRIORITY = Integer.MAX_VALUE;
51     private static final int DEFAULT_PRIORITY = 0;
52 
53     private final ILights mHal = new ILights.Stub() {
54         @Override
55         public void setLightState(int id, HwLightState state) {
56             return;
57         }
58 
59         @Override
60         public HwLight[] getLights() {
61             return new HwLight[] {
62                 fakeHwLight(101, 3, 1),
63                 fakeHwLight(102, LightsManager.LIGHT_TYPE_MICROPHONE, 4),
64                 fakeHwLight(103, LightsManager.LIGHT_TYPE_MICROPHONE, 3),
65                 fakeHwLight(104, LightsManager.LIGHT_TYPE_MICROPHONE, 1),
66                 fakeHwLight(105, LightsManager.LIGHT_TYPE_MICROPHONE, 2)
67             };
68         }
69 
70         @Override
71         public int getInterfaceVersion() {
72             return this.VERSION;
73         }
74 
75         @Override
76         public String getInterfaceHash() {
77             return this.HASH;
78         }
79     };
80 
fakeHwLight(int id, int type, int ordinal)81     private static HwLight fakeHwLight(int id, int type, int ordinal) {
82         HwLight light = new HwLight();
83         light.id = id;
84         light.type = (byte) type;
85         light.ordinal = ordinal;
86         return light;
87     }
88 
89     @Mock
90     Context mContext;
91 
92     @Before
setUp()93     public void setUp() {
94         MockitoAnnotations.initMocks(this);
95     }
96 
97     @Test
testGetLights_filtersSystemLights()98     public void testGetLights_filtersSystemLights() {
99         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
100         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
101 
102         // When lights are listed, only the 4 MICROPHONE lights should be visible.
103         assertThat(manager.getLights().size()).isEqualTo(4);
104     }
105 
106     @Test
testControlMultipleLights()107     public void testControlMultipleLights() {
108         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
109         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
110 
111         // When the session requests to turn 3/4 lights on:
112         LightsManager.LightsSession session = manager.openSession();
113         session.requestLights(new Builder()
114                 .addLight(manager.getLights().get(0), new LightState(0xf1))
115                 .addLight(manager.getLights().get(1), new LightState(0xf2))
116                 .addLight(manager.getLights().get(2), new LightState(0xf3))
117                 .build());
118 
119         // Then all 3 should turn on.
120         assertThat(manager.getLightState(manager.getLights().get(0)).getColor()).isEqualTo(0xf1);
121         assertThat(manager.getLightState(manager.getLights().get(1)).getColor()).isEqualTo(0xf2);
122         assertThat(manager.getLightState(manager.getLights().get(2)).getColor()).isEqualTo(0xf3);
123 
124         // And the 4th should remain off.
125         assertThat(manager.getLightState(manager.getLights().get(3)).getColor()).isEqualTo(0x00);
126     }
127 
128     @Test
testControlLights_onlyEffectiveForLifetimeOfClient()129     public void testControlLights_onlyEffectiveForLifetimeOfClient() throws Exception {
130         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
131         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
132         Light micLight = manager.getLights().get(0);
133 
134         // The light should begin by being off.
135         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
136 
137         // When a session commits changes:
138         LightsManager.LightsSession session = manager.openSession();
139         session.requestLights(new Builder().addLight(micLight, new LightState(GREEN)).build());
140         // Then the light should turn on.
141         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN);
142 
143         // When the session goes away:
144         session.close();
145 
146         // Then the light should turn off.
147         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
148     }
149 
150     @Test
testControlLights_firstCallerWinsContention()151     public void testControlLights_firstCallerWinsContention() throws Exception {
152         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
153         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
154         Light micLight = manager.getLights().get(0);
155 
156         LightsManager.LightsSession session1 = manager.openSession();
157         LightsManager.LightsSession session2 = manager.openSession();
158 
159         // When session1 and session2 both request the same light:
160         session1.requestLights(new Builder().addLight(micLight, new LightState(BLUE)).build());
161         session2.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
162         // Then session1 should win because it was created first.
163         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE);
164 
165         // When session1 goes away:
166         session1.close();
167 
168         // Then session2 should have its request go into effect.
169         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE);
170 
171         // When session2 goes away:
172         session2.close();
173 
174         // Then the light should turn off because there are no more sessions.
175         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
176     }
177 
178     @Test
testClearLight()179     public void testClearLight() {
180         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
181         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
182         Light micLight = manager.getLights().get(0);
183 
184         // When the session turns a light on:
185         LightsManager.LightsSession session = manager.openSession();
186         session.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
187 
188         // And then the session clears it again:
189         session.requestLights(new Builder().clearLight(micLight).build());
190 
191         // Then the light should turn back off.
192         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
193     }
194 
195     @Test
testControlLights_higherPriorityCallerWinsContention()196     public void testControlLights_higherPriorityCallerWinsContention() throws Exception {
197         LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
198         LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
199         Light micLight = manager.getLights().get(0);
200 
201         // The light should begin by being off.
202         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
203 
204         try (LightsManager.LightsSession session1 = manager.openSession(DEFAULT_PRIORITY)) {
205             try (LightsManager.LightsSession session2 = manager.openSession(HIGH_PRIORITY)) {
206                 // When session1 and session2 both request the same light:
207                 session1.requestLights(
208                         new Builder().addLight(micLight, new LightState(BLUE)).build());
209                 session2.requestLights(
210                         new Builder().addLight(micLight, new LightState(WHITE)).build());
211                 // Then session2 should win because it has a higher priority.
212                 assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE);
213             }
214             // Then session1 should have its request go into effect.
215             assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE);
216         }
217         // Then the light should turn off because there are no more sessions.
218         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
219     }
220 }
221