1 /*
2  * Copyright (C) 2022 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.car.evs;
18 
19 import static android.car.evs.CarEvsManager.ERROR_NONE;
20 import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE;
21 import static android.car.feature.Flags.FLAG_CAR_EVS_STREAM_MANAGEMENT;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 import static com.google.common.truth.Truth.assertWithMessage;
25 
26 import static org.junit.Assert.assertThrows;
27 import static org.mockito.Mockito.any;
28 import static org.mockito.Mockito.anyInt;
29 import static org.mockito.Mockito.anyString;
30 import static org.mockito.Mockito.atLeastOnce;
31 import static org.mockito.Mockito.doNothing;
32 import static org.mockito.Mockito.doThrow;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36 
37 import android.car.evs.CarEvsManager.CarEvsStatusListener;
38 import android.car.evs.CarEvsManager.CarEvsStreamCallback;
39 import android.car.feature.FakeFeatureFlagsImpl;
40 import android.hardware.HardwareBuffer;
41 import android.os.IBinder;
42 import android.os.RemoteException;
43 import android.platform.test.annotations.IgnoreUnderRavenwood;
44 import android.platform.test.ravenwood.RavenwoodRule;
45 
46 import com.android.car.internal.ICarBase;
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.ArgumentCaptor;
54 import org.mockito.Captor;
55 import org.mockito.Mock;
56 import org.mockito.junit.MockitoJUnitRunner;
57 
58 /**
59  * <p>This class contains unit tests for the {@link CarEvsManager}.
60  */
61 @RunWith(MockitoJUnitRunner.class)
62 public final class CarEvsManagerUnitTest {
63 
64     @Rule
65     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
66 
67     private static final int[] SERVICE_TYPES = {
68         CarEvsManager.SERVICE_TYPE_REARVIEW, CarEvsManager.SERVICE_TYPE_SURROUNDVIEW,
69         CarEvsManager.SERVICE_TYPE_FRONTVIEW, CarEvsManager.SERVICE_TYPE_LEFTVIEW,
70         CarEvsManager.SERVICE_TYPE_RIGHTVIEW, CarEvsManager.SERVICE_TYPE_DRIVERVIEW,
71         CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW,
72         CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW,
73     };
74 
75     @Mock private ICarBase mMockCar;
76     @Mock private CarEvsStatusListener mMockCarEvsStatusListener;
77     @Mock private CarEvsStreamCallback mMockCarEvsStreamCallback;
78     @Mock private IBinder mMockBinder;
79     @Mock private ICarEvsService.Stub mMockICarEvsService;
80 
81     @Captor private ArgumentCaptor<ICarEvsStatusListener> mCarEvsStatusListenerCaptor;
82     @Captor private ArgumentCaptor<ICarEvsStreamCallback> mCarEvsStreamCallbackCaptor;
83     @Captor private ArgumentCaptor<CarEvsStatus> mCarEvsStatusCaptor;
84 
85     private final FakeFeatureFlagsImpl mFakeFeatureFlags = new FakeFeatureFlagsImpl();
86     private CarEvsManager mManager;
87 
88     @Before
setUp()89     public void setUp() throws Exception {
90         mFakeFeatureFlags.setFlag(FLAG_CAR_EVS_STREAM_MANAGEMENT, true);
91         when(mMockBinder.queryLocalInterface(anyString())).thenReturn(mMockICarEvsService);
92 
93         mManager = new CarEvsManager(mMockCar, mMockBinder, mFakeFeatureFlags);
94         assertThat(mManager).isNotNull();
95     }
96 
97     @After
tearDown()98     public void tearDown() throws Exception {
99         mManager = null;
100     }
101 
102     @Test
testSetStatusListenerWithInvalidArguments()103     public void testSetStatusListenerWithInvalidArguments() {
104         assertThrows(NullPointerException.class,
105                 () -> mManager.setStatusListener(/* executor= */ null, /* listener= */ null));
106         assertThrows(NullPointerException.class,
107                 () -> mManager.setStatusListener(/* executor= */ null, mMockCarEvsStatusListener));
108     }
109 
110     @Test
testSetStatusListenerWithValidArguments()111     public void testSetStatusListenerWithValidArguments() throws Exception {
112         mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
113         verify(mMockICarEvsService, atLeastOnce()).registerStatusListener(any());
114     }
115 
116     @Test
testSetStatusListenerWithValidArgumentsTwice()117     public void testSetStatusListenerWithValidArgumentsTwice() throws Exception {
118         mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
119 
120         verify(mMockICarEvsService, atLeastOnce()).registerStatusListener(any());
121         IllegalStateException thrown = assertThrows(IllegalStateException.class,
122                 () -> mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener));
123         assertWithMessage("Register CarEvsStatusListener")
124                 .that(thrown).hasMessageThat()
125                 .contains("A status listener is already registered.");
126     }
127 
128     @Test
testSetStatusListenerWithValidArgumentsRemoteExceptionThrown()129     public void testSetStatusListenerWithValidArgumentsRemoteExceptionThrown() throws Exception {
130         doThrow(new RemoteException()).when(mMockICarEvsService).registerStatusListener(any());
131 
132         mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
133 
134         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
135     }
136 
137     @Test
testStatusChangedEvent()138     public void testStatusChangedEvent() throws Exception {
139         doNothing().when(mMockICarEvsService)
140                 .registerStatusListener(mCarEvsStatusListenerCaptor.capture());
141 
142         mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
143         ICarEvsStatusListener listener = mCarEvsStatusListenerCaptor.getValue();
144         listener.onStatusChanged(new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
145                                                   CarEvsManager.SERVICE_STATE_INACTIVE));
146 
147         verify(mMockCarEvsStatusListener, atLeastOnce())
148                 .onStatusChanged(mCarEvsStatusCaptor.capture());
149         CarEvsStatus received = mCarEvsStatusCaptor.getValue();
150         assertThat(received.describeContents()).isEqualTo(0);
151         assertThat(received.toString()).contains("CarEvsStatus:");
152     }
153 
154     @Test
testClearStatusListener()155     public void testClearStatusListener() throws Exception {
156         doNothing().when(mMockICarEvsService)
157                 .registerStatusListener(mCarEvsStatusListenerCaptor.capture());
158 
159         mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
160         ICarEvsStatusListener listener = mCarEvsStatusListenerCaptor.getValue();
161         mManager.clearStatusListener();
162 
163         verify(mMockICarEvsService, atLeastOnce()).unregisterStatusListener(listener);
164     }
165 
166     @Test
testClearStatusListenerRemoteExceptionThrown()167     public void testClearStatusListenerRemoteExceptionThrown() throws Exception {
168         doThrow(new RemoteException()).when(mMockICarEvsService).unregisterStatusListener(any());
169 
170         mManager.clearStatusListener();
171 
172         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
173     }
174 
175     @Test
176     @IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
testStartVideoStreamWithoutToken()177     public void testStartVideoStreamWithoutToken() throws Exception {
178         when(mMockICarEvsService
179                 .startVideoStream(anyInt(), any(), mCarEvsStreamCallbackCaptor.capture()))
180                 .thenReturn(ERROR_NONE);
181 
182         assertThat(mManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
183                                              /* token= */ null, Runnable::run,
184                                              mMockCarEvsStreamCallback))
185                 .isEqualTo(ERROR_NONE);
186 
187         ICarEvsStreamCallback cb = mCarEvsStreamCallbackCaptor.getValue();
188         cb.onStreamEvent(CarEvsManager.SERVICE_TYPE_REARVIEW,
189                 CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
190         verify(mMockCarEvsStreamCallback, atLeastOnce()).onStreamEvent(
191                 CarEvsManager.SERVICE_TYPE_REARVIEW, CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
192 
193         int bufferId = 1;
194         HardwareBuffer hwbuffer =
195                 HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
196                                       /* format= */ HardwareBuffer.RGBA_8888,
197                                       /* layers= */ 1,
198                                       /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
199         CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId,
200                 CarEvsManager.SERVICE_TYPE_REARVIEW, hwbuffer);
201         cb.onNewFrame(buffer);
202         verify(mMockCarEvsStreamCallback, atLeastOnce()).onNewFrame(buffer);
203     }
204 
205     @Test
testStartVideoStreamWithoutTokenRemoteExceptionThrown()206     public void testStartVideoStreamWithoutTokenRemoteExceptionThrown() throws Exception {
207         when(mMockICarEvsService.startVideoStream(anyInt(), any(), any()))
208                 .thenThrow(new RemoteException());
209 
210         assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
211                                              /* token= */ null,
212                                              /* executor= */ Runnable::run,
213                                              /* callback= */ mMockCarEvsStreamCallback))
214                 .isEqualTo(ERROR_UNAVAILABLE);
215 
216         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
217     }
218 
219     @Test
testStopVideoStream()220     public void testStopVideoStream() throws Exception {
221         mManager.stopVideoStream();
222         verify(mMockICarEvsService, never()).stopVideoStream(any());
223 
224         assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
225                                              /* token= */ null,
226                                              /* executor= */ Runnable::run,
227                                              /* callback= */ mMockCarEvsStreamCallback))
228                 .isEqualTo(ERROR_NONE);
229 
230         mManager.stopVideoStream();
231 
232         verify(mMockICarEvsService, atLeastOnce()).stopVideoStream(any());
233     }
234 
235     @Test
testStopVideoStreamRemoteExceptionThrown()236     public void testStopVideoStreamRemoteExceptionThrown() throws Exception {
237         doThrow(new RemoteException()).when(mMockICarEvsService).stopVideoStream(any());
238 
239         assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
240                                              /* token= */ null,
241                                              /* executor= */ Runnable::run,
242                                              /* callback= */ mMockCarEvsStreamCallback))
243                 .isEqualTo(ERROR_NONE);
244         mManager.stopVideoStream();
245 
246         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
247     }
248 
249     @Test
testGetCurrentStatus()250     public void testGetCurrentStatus() throws Exception {
251         when(mMockICarEvsService.getCurrentStatus(anyInt()))
252                 .thenReturn(new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
253                                              CarEvsManager.SERVICE_STATE_INACTIVE));
254 
255         CarEvsStatus currentStatus = mManager.getCurrentStatus();
256 
257         assertThat(currentStatus).isNotNull();
258         assertThat(currentStatus.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
259         assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
260     }
261 
262     @Test
testGetCurrentStatusWithType()263     public void testGetCurrentStatusWithType() throws Exception {
264         for (int type : SERVICE_TYPES) {
265             when(mMockICarEvsService.getCurrentStatus(anyInt()))
266                     .thenReturn(new CarEvsStatus(type, CarEvsManager.SERVICE_STATE_INACTIVE));
267 
268             CarEvsStatus currentStatus = mManager.getCurrentStatus(type);
269 
270             assertThat(currentStatus).isNotNull();
271             assertThat(currentStatus.getServiceType()).isEqualTo(type);
272             assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
273         }
274     }
275 
276     @Test
testGetCurrentStatusRemoteExceptionThrown()277     public void testGetCurrentStatusRemoteExceptionThrown() throws Exception {
278         when(mMockICarEvsService.getCurrentStatus(anyInt())).thenThrow(new RemoteException());
279 
280         CarEvsStatus currentStatus = mManager.getCurrentStatus();
281 
282         assertThat(currentStatus.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
283         assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
284     }
285 
286     @Test
testGetCurrentStatusWithTypeRemoteExceptionThrown()287     public void testGetCurrentStatusWithTypeRemoteExceptionThrown() throws Exception {
288         when(mMockICarEvsService.getCurrentStatus(anyInt())).thenThrow(new RemoteException());
289 
290         for (int type : SERVICE_TYPES) {
291             assertThat(mManager.getCurrentStatus(type)).isNull();
292         }
293     }
294 
295     @Test
testGenerateSessionToken()296     public void testGenerateSessionToken() throws Exception {
297         assertThat(mManager.generateSessionToken()).isNotNull();
298 
299         when(mMockICarEvsService.generateSessionToken()).thenReturn(null);
300         assertThat(mManager.generateSessionToken()).isNotNull();
301     }
302 
303     @Test
testGenerateSessionTokenRemoteExceptionThrown()304     public void testGenerateSessionTokenRemoteExceptionThrown() throws Exception {
305         when(mMockICarEvsService.generateSessionToken()).thenThrow(new RemoteException());
306 
307         assertThat(mManager.generateSessionToken()).isNotNull();
308     }
309 
310     @Test
testIsSupported()311     public void testIsSupported() throws Exception {
312         when(mMockICarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).thenReturn(true);
313         when(mMockICarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW))
314                 .thenReturn(false);
315 
316         assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isTrue();
317         assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW)).isFalse();
318     }
319 
320     @Test
testIsSupportedRemoteExceptionThrown()321     public void testIsSupportedRemoteExceptionThrown() throws Exception {
322         when(mMockICarEvsService.isSupported(anyInt())).thenThrow(new RemoteException());
323 
324         assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isFalse();
325     }
326 
327     @Test
328     @IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
testReturnFrameBuffer()329     public void testReturnFrameBuffer() throws Exception {
330         int bufferId = 1;
331         HardwareBuffer hwbuffer =
332                 HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
333                                       /* format= */ HardwareBuffer.RGBA_8888,
334                                       /* layers= */ 1,
335                                       /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
336         CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId, hwbuffer);
337 
338         mManager.returnFrameBuffer(buffer);
339 
340         verify(mMockICarEvsService, atLeastOnce()).returnFrameBuffer(buffer);
341     }
342 
343     @Test
344     @IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
testReturnFrameBufferRemoteExceptionThrown()345     public void testReturnFrameBufferRemoteExceptionThrown() throws Exception {
346         doThrow(new RemoteException()).when(mMockICarEvsService).returnFrameBuffer(any());
347         int bufferId = 1;
348         HardwareBuffer hwbuffer =
349                 HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
350                                       /* format= */ HardwareBuffer.RGBA_8888,
351                                       /* layers= */ 1,
352                                       /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
353         CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId, hwbuffer);
354 
355         mManager.returnFrameBuffer(buffer);
356 
357         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
358     }
359 
360     @Test
testStartActivity()361     public void testStartActivity() throws Exception {
362         when(mMockICarEvsService.startActivity(anyInt())).thenReturn(ERROR_NONE);
363 
364         assertThat(mManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
365                 .isEqualTo(ERROR_NONE);
366 
367         verify(mMockICarEvsService, atLeastOnce()).startActivity(anyInt());
368     }
369 
370     @Test
testStartActivityRemoteExceptionThrown()371     public void testStartActivityRemoteExceptionThrown() throws Exception {
372         when(mMockICarEvsService.startActivity(anyInt())).thenThrow(new RemoteException());
373 
374         assertThat(mManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
375                 .isEqualTo(ERROR_UNAVAILABLE);
376 
377         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
378     }
379 
380     @Test
testStopActivity()381     public void testStopActivity() throws Exception {
382         mManager.stopActivity();
383 
384         verify(mMockICarEvsService, atLeastOnce()).stopActivity();
385     }
386 
387     @Test
testStopActivityRemoteExceptionThrown()388     public void testStopActivityRemoteExceptionThrown() throws Exception {
389         doThrow(new RemoteException()).when(mMockICarEvsService).stopActivity();
390 
391         mManager.stopActivity();
392 
393         verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
394     }
395 
396     @Test
testOnCarDisconnected()397     public void testOnCarDisconnected() {
398         mManager.onCarDisconnected();
399     }
400 
401     @Test
testCarEvsStatus()402     public void testCarEvsStatus() {
403         CarEvsStatus[] arr = CarEvsStatus.CREATOR.newArray(3);
404         assertThat(arr.length).isEqualTo(3);
405 
406         CarEvsStatus original = new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
407                                                  CarEvsManager.SERVICE_STATE_INACTIVE);
408         assertThat(original.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
409         assertThat(original.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
410 
411         android.os.Parcel packet = android.os.Parcel.obtain();
412         original.writeToParcel(packet, /* flags= */ 0);
413         assertThat(CarEvsStatus.CREATOR.createFromParcel(packet)).isNotNull();
414     }
415 
416     @Test
417     @IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
testCarEvsBufferDescriptor()418     public void testCarEvsBufferDescriptor() {
419         CarEvsBufferDescriptor[] arr = CarEvsBufferDescriptor.CREATOR.newArray(3);
420         assertThat(arr.length).isEqualTo(3);
421 
422         int bufferId = 1;
423         HardwareBuffer hwbuffer =
424                 HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
425                                       /* format= */ HardwareBuffer.RGBA_8888,
426                                       /* layers= */ 1,
427                                       /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
428         CarEvsBufferDescriptor original = new CarEvsBufferDescriptor(bufferId, hwbuffer);
429 
430         assertThat(original.getId()).isEqualTo(bufferId);
431         assertThat(original.getHardwareBuffer().getWidth()).isEqualTo(64);
432         assertThat(original.getHardwareBuffer().getHeight()).isEqualTo(32);
433         assertThat(original.getHardwareBuffer().getFormat()).isEqualTo(HardwareBuffer.RGBA_8888);
434 
435         android.os.Parcel packet = android.os.Parcel.obtain();
436         original.writeToParcel(packet, /* flags= */ 0);
437         packet.setDataPosition(0);
438         assertThat(CarEvsBufferDescriptor.CREATOR.createFromParcel(packet)).isNotNull();
439     }
440 }
441