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.CarEvsStreamEvent;
27 import android.car.evs.CarEvsStatus;
28 import android.os.SystemClock;
29 import android.util.Log;
30 
31 import androidx.test.core.app.ApplicationProvider;
32 import androidx.test.filters.MediumTest;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 import java.util.ArrayList;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Executors;
43 import java.util.concurrent.Semaphore;
44 import java.util.concurrent.TimeUnit;
45 
46 /*
47  * IMPORTANT NOTE:
48  * This test assumes that EVS HAL is running at the time of test.  Depending on the test target, the
49  * reference EVS HAL ($ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver) may be needed.
50  * Please add below line to the target's build script to add the reference EVS HAL to the build:
51  * ENABLE_EVS_SAMPLE := true
52  *
53  * The test will likely fail if no EVS HAL is running on the target device.
54  */
55 @RunWith(AndroidJUnit4.class)
56 @MediumTest
57 public final class CarEvsManagerTest extends MockedCarTestBase {
58     private static final String TAG = CarEvsManagerTest.class.getSimpleName();
59 
60     // We'd expect that underlying stream runs @10fps at least.
61     private static final int NUMBER_OF_FRAMES_TO_WAIT = 10;
62     private static final int FRAME_TIMEOUT_MS = 1000;
63     private static final int SMALL_NAP_MS = 500;
64     private static final int STREAM_EVENT_TIMEOUT_SEC = 2;
65 
66     // Will return frame buffers in the order they arrived.
67     private static final int INDEX_TO_FIRST_ELEM = 0;
68 
69     private final ArrayList<CarEvsBufferDescriptor> mReceivedBuffers = new ArrayList<>();
70     private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
71     private final Semaphore mFrameReceivedSignal = new Semaphore(0);
72     private final Semaphore mStreamEventOccurred = new Semaphore(0);
73 
74     private final Car mCar = Car.createCar(ApplicationProvider.getApplicationContext());
75     private final CarEvsManager mEvsManager =
76             (CarEvsManager) mCar.getCarManager(Car.CAR_EVS_SERVICE);
77     private final EvsStreamCallbackImpl mStreamCallback = new EvsStreamCallbackImpl();
78     private final EvsStatusListenerImpl mStatusListener = new EvsStatusListenerImpl();
79 
80     private @CarEvsStreamEvent int mLastStreamEvent;
81 
82     @Before
setUp()83     public void setUp() {
84         assumeTrue(mCar.isFeatureEnabled(Car.CAR_EVS_SERVICE));
85         assertThat(mEvsManager).isNotNull();
86         assumeTrue(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW));
87         assertThat(mStreamCallback).isNotNull();
88         assertThat(mStatusListener).isNotNull();
89 
90         // Drains all permits
91         mFrameReceivedSignal.drainPermits();
92         mStreamEventOccurred.drainPermits();
93 
94         // Ensures no stream is active
95         mEvsManager.stopVideoStream();
96     }
97 
98     @After
tearDown()99     public void tearDown() throws Exception {
100         if (mEvsManager != null) {
101             mEvsManager.stopVideoStream();
102         }
103     }
104 
105     @Test
testSessionTokenGeneration()106     public void testSessionTokenGeneration() throws Exception {
107         assertThat(mEvsManager.generateSessionToken()).isNotNull();
108     }
109 
110     @Test
testStartAndStopVideoStream()111     public void testStartAndStopVideoStream() throws Exception {
112         // Registers a status listener and start monitoring the CarEvsService's state changes.
113         mEvsManager.setStatusListener(mCallbackExecutor, mStatusListener);
114 
115         // Requests to start a video stream.
116         assertThat(
117                 mEvsManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
118                         /* token = */ null, mCallbackExecutor, mStreamCallback)
119         ).isEqualTo(CarEvsManager.ERROR_NONE);
120 
121         // Waits for a few frame buffers
122         for (int i = 0; i < NUMBER_OF_FRAMES_TO_WAIT; ++i) {
123             assertThat(
124                     mFrameReceivedSignal.tryAcquire(FRAME_TIMEOUT_MS, TimeUnit.MILLISECONDS)
125             ).isTrue();
126 
127             // Nothing to do; returns a buffer immediately
128             CarEvsBufferDescriptor toBeReturned = mReceivedBuffers.get(INDEX_TO_FIRST_ELEM);
129             mReceivedBuffers.remove(INDEX_TO_FIRST_ELEM);
130             mEvsManager.returnFrameBuffer(toBeReturned);
131         }
132 
133         // Checks a current status
134         CarEvsStatus status = mEvsManager.getCurrentStatus();
135         assertThat(status).isNotNull();
136         assertThat(status.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_ACTIVE);
137         assertThat(status.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
138 
139         // Then, requests to stop a video stream
140         mEvsManager.stopVideoStream();
141 
142         // Checks a current status a few hundreds milliseconds after.  CarEvsService will move into
143         // the inactive state when it gets a stream-stopped event from the EVS manager.
144         SystemClock.sleep(SMALL_NAP_MS);
145         assertThat(mStreamCallback.waitForStreamEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED))
146                 .isTrue();
147 
148         // Unregister a listener
149         mEvsManager.clearStatusListener();
150     }
151 
152     @Test
testIsSupported()153     public void testIsSupported() throws Exception {
154         assertThat(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isTrue();
155         // TODO(b/179029031): Fix below test when the Surround View service is integrated into
156         // CarEvsService.
157         assertThat(mEvsManager.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW)).isFalse();
158     }
159 
160     /**
161      * Class that implements the listener interface and gets called back from
162      * {@link android.car.evs.CarEvsManager.CarEvsStatusListener}.
163      */
164     private static final class EvsStatusListenerImpl implements CarEvsManager.CarEvsStatusListener {
165         @Override
onStatusChanged(CarEvsStatus status)166         public void onStatusChanged(CarEvsStatus status) {
167             Log.i(TAG, "Received a notification of status changed to " + status.getState());
168         }
169     }
170 
171     /**
172      * Class that implements the listener interface and gets called back from
173      * {@link android.hardware.automotive.evs.IEvsCameraStream}.
174      */
175     private final class EvsStreamCallbackImpl implements CarEvsManager.CarEvsStreamCallback {
176         @Override
onStreamEvent(@arEvsStreamEvent int event)177         public void onStreamEvent(@CarEvsStreamEvent int event) {
178             mLastStreamEvent = event;
179             mStreamEventOccurred.release();
180         }
181 
182         @Override
onNewFrame(CarEvsBufferDescriptor buffer)183         public void onNewFrame(CarEvsBufferDescriptor buffer) {
184             // Enqueues a new frame
185             mReceivedBuffers.add(buffer);
186 
187             // Notifies a new frame's arrival
188             mFrameReceivedSignal.release();
189         }
190 
waitForStreamEvent(@arEvsStreamEvent int expected)191         public boolean waitForStreamEvent(@CarEvsStreamEvent int expected) {
192             while (mLastStreamEvent != expected) {
193                 try {
194                     if (!mStreamEventOccurred.tryAcquire(STREAM_EVENT_TIMEOUT_SEC,
195                             TimeUnit.SECONDS)) {
196                         Log.e(TAG, "No stream event is received before the timer expired.");
197                         return false;
198                     }
199                 } catch (InterruptedException e) {
200                     Log.e(TAG, "Current waiting thread is interrupted. ", e);
201                     return false;
202                 }
203             }
204 
205             return true;
206         }
207     }
208 }
209