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 com.android.car;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assume.assumeTrue;
22 
23 import android.car.Car;
24 import android.car.evs.CarEvsBufferDescriptor;
25 import android.car.evs.CarEvsManager;
26 import android.car.evs.CarEvsManager.CarEvsServiceState;
27 import android.car.evs.CarEvsManager.CarEvsServiceType;
28 import android.car.evs.CarEvsManager.CarEvsStreamEvent;
29 import android.car.evs.CarEvsStatus;
30 import android.content.Context;
31 import android.os.SystemClock;
32 import android.test.suitebuilder.annotation.MediumTest;
33 import android.util.Log;
34 
35 import androidx.test.core.app.ApplicationProvider;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 
43 import java.util.ArrayList;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.ExecutorService;
46 import java.util.concurrent.Executors;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.TimeUnit;
49 
50 /*
51  * IMPORTANT NOTE:
52  * This test assumes that EVS HAL is running at the time of test.  Depending on the test target, the
53  * reference EVS HAL ($ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver) may be needed.
54  * Please add below line to the target's build script to add the reference EVS HAL to the build:
55  * ENABLE_EVS_SAMPLE := true
56  *
57  * The test will likely fail if no EVS HAL is running on the target device.
58  */
59 @RunWith(AndroidJUnit4.class)
60 @MediumTest
61 public final class CarEvsManagerTest extends MockedCarTestBase {
62     private static final String TAG = CarEvsManagerTest.class.getSimpleName();
63 
64     // We'd expect that underlying stream runs @10fps at least.
65     private static final int NUMBER_OF_FRAMES_TO_WAIT = 10;
66     private static final int FRAME_TIMEOUT_MS = 1000;
67     private static final int SMALL_NAP_MS = 500;
68     private static final int ACTIVITY_REQUEST_TIMEOUT_SEC = 3;
69 
70     // Will return frame buffers in the order they arrived.
71     private static final int INDEX_TO_FIRST_ELEM = 0;
72 
73     private final ArrayList<CarEvsBufferDescriptor> mReceivedBuffers = new ArrayList<>();
74     private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
75     private final Semaphore mFrameReceivedSignal = new Semaphore(0);
76 
77     private final Car mCar = Car.createCar(ApplicationProvider.getApplicationContext());
78     private final CarEvsManager mEvsManager =
79             (CarEvsManager) mCar.getCarManager(Car.CAR_EVS_SERVICE);
80     private final EvsStreamCallbackImpl mStreamCallback = new EvsStreamCallbackImpl();
81     private final EvsStatusListenerImpl mStatusListener = new EvsStatusListenerImpl();
82 
83     private CountDownLatch mActivityRequested;
84 
85     @Before
setUp()86     public void setUp() {
87         assumeTrue(mCar.isFeatureEnabled(Car.CAR_EVS_SERVICE));
88         assertThat(mEvsManager).isNotNull();
89         assumeTrue(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW));
90         assertThat(mStreamCallback).isNotNull();
91         assertThat(mStatusListener).isNotNull();
92 
93         // Ensures no stream is active
94         mEvsManager.stopVideoStream();
95     }
96 
97     @After
tearDown()98     public void tearDown() throws Exception {
99         if (mEvsManager != null) {
100             mEvsManager.stopVideoStream();
101         }
102     }
103 
104     @Test
testSessionTokenGeneration()105     public void testSessionTokenGeneration() throws Exception {
106         assertThat(mEvsManager.generateSessionToken()).isNotNull();
107     }
108 
109     @Test
testSetStatusListener()110     public void testSetStatusListener() throws Exception {
111         // Set a status listener
112         mEvsManager.setStatusListener(mCallbackExecutor, mStatusListener);
113 
114         // Request to start a service
115         assertThat(
116                 mEvsManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW)
117         ).isEqualTo(CarEvsManager.ERROR_NONE);
118 
119         // Wait for a notification
120         mActivityRequested = new CountDownLatch(1);
121         assertThat(mActivityRequested.await(ACTIVITY_REQUEST_TIMEOUT_SEC, TimeUnit.SECONDS))
122                 .isTrue();
123 
124         // Then, requests to stop a video stream started upon a receipt of STATE_REQUESTED
125         // transition notification.
126         mEvsManager.stopVideoStream();
127 
128         // Unregister a listener
129         mEvsManager.clearStatusListener();
130     }
131 
132     @Test
testStartAndStopVideoStream()133     public void testStartAndStopVideoStream() throws Exception {
134         // Requests to start a video stream.  We're intentionally using the listener that
135         // is registered during the test setup.
136         assertThat(
137                 mEvsManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
138                         /* token = */ null, mCallbackExecutor, mStreamCallback)
139         ).isEqualTo(CarEvsManager.ERROR_NONE);
140 
141         // Waits for a few frames frame buffers
142         for (int i = 0; i < NUMBER_OF_FRAMES_TO_WAIT; ++i) {
143             assertThat(
144                     mFrameReceivedSignal.tryAcquire(
145                             FRAME_TIMEOUT_MS, TimeUnit.MILLISECONDS)
146             ).isTrue();
147 
148             // Nothing to do; returns a buffer immediately
149             CarEvsBufferDescriptor toBeReturned = mReceivedBuffers.get(INDEX_TO_FIRST_ELEM);
150             mReceivedBuffers.remove(INDEX_TO_FIRST_ELEM);
151             mEvsManager.returnFrameBuffer(toBeReturned);
152         }
153 
154         // Checks a current status
155         CarEvsStatus status = mEvsManager.getCurrentStatus();
156         assertThat(status).isNotNull();
157         assertThat(status.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
158         assertThat(status.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
159 
160         // Then, requests to stop a video stream
161         mEvsManager.stopVideoStream();
162 
163         // Checks a current status a few hundreds milliseconds after.  CarEvsService will move into
164         // the inactive state when it gets a stream-stopped event from the EVS manager.
165         SystemClock.sleep(SMALL_NAP_MS);
166         status = mEvsManager.getCurrentStatus();
167         assertThat(status).isNotNull();
168         assertThat(status.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
169 
170         // Unregister a listener
171         mEvsManager.clearStatusListener();
172     }
173 
174     @Test
testIsSupported()175     public void testIsSupported() throws Exception {
176         assertThat(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isTrue();
177         // TODO(b/179029031): Fix below test when the Surround View service is integrated into
178         // CarEvsService.
179         assertThat(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW)).isFalse();
180     }
181 
182     /**
183      * Class that implements the listener interface and gets called back from
184      * {@link android.car.evs.CarEvsManager.CarEvsStatusListener}.
185      */
186     private final class EvsStatusListenerImpl implements CarEvsManager.CarEvsStatusListener {
187         @Override
onStatusChanged(CarEvsStatus status)188         public void onStatusChanged(CarEvsStatus status) {
189             switch (status.getState()) {
190                 case CarEvsManager.SERVICE_STATE_REQUESTED:
191                     // Request to start a video stream
192                     assertThat(
193                             mEvsManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
194                                     /* token = */ null, mCallbackExecutor, mStreamCallback)
195                     ).isEqualTo(CarEvsManager.ERROR_NONE);
196                     mActivityRequested.countDown();
197                     break;
198 
199                 case CarEvsManager.SERVICE_STATE_ACTIVE:
200                     // Nothing to do
201                     break;
202 
203                 case CarEvsManager.SERVICE_STATE_UNAVAILABLE:
204                     // Nothing to do
205                     break;
206 
207                 default:
208                     // Nothing to do
209                     break;
210             }
211         }
212     }
213 
214     /**
215      * Class that implements the listener interface and gets called back from
216      * {@link android.hardware.automotive.evs.IEvsCameraStream}.
217      */
218     private final class EvsStreamCallbackImpl implements CarEvsManager.CarEvsStreamCallback {
219         @Override
onStreamEvent(@arEvsStreamEvent int event)220         public void onStreamEvent(@CarEvsStreamEvent int event) {
221             switch(event) {
222                 case CarEvsManager.STREAM_EVENT_STREAM_STARTED:
223                     // Ignores this event for now because our reference EVS HAL does not send this
224                     // event.
225                     break;
226 
227                 case CarEvsManager.STREAM_EVENT_STREAM_STOPPED:
228                     break;
229 
230                 default:
231                     // Ignores other stream events in this test.
232                     break;
233             }
234         }
235 
236         @Override
onNewFrame(CarEvsBufferDescriptor buffer)237         public void onNewFrame(CarEvsBufferDescriptor buffer) {
238             // Enqueues a new frame
239             mReceivedBuffers.add(buffer);
240 
241             // Notifies a new frame's arrival
242             mFrameReceivedSignal.release();
243         }
244     }
245 }
246