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 package com.android.car;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import android.car.occupantawareness.IOccupantAwarenessEventCallback;
21 import android.car.occupantawareness.OccupantAwarenessDetection;
22 import android.car.occupantawareness.SystemStatusEvent;
23 import android.content.Context;
24 import android.hardware.automotive.occupant_awareness.IOccupantAwareness;
25 import android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback;
26 import android.hardware.automotive.occupant_awareness.OccupantAwarenessStatus;
27 import android.hardware.automotive.occupant_awareness.OccupantDetection;
28 import android.hardware.automotive.occupant_awareness.OccupantDetections;
29 import android.hardware.automotive.occupant_awareness.Role;
30 import android.os.RemoteException;
31 import android.test.suitebuilder.annotation.MediumTest;
32 
33 import androidx.test.core.app.ApplicationProvider;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import junit.framework.TestCase;
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.concurrent.CompletableFuture;
44 import java.util.concurrent.TimeUnit;
45 
46 @RunWith(AndroidJUnit4.class)
47 @MediumTest
48 public class OccupantAwarenessSystemServiceTest extends TestCase {
49     private static final int TIMESTAMP = 1234; // In milliseconds.
50 
51     /**
52      * Mock implementation of {@link
53      * android.hardware.automotive.occupant_awareness.IOccupantAwareness} for testing the service
54      * and manager.
55      */
56     private class MockOasHal
57             extends android.hardware.automotive.occupant_awareness.IOccupantAwareness.Stub {
58         private IOccupantAwarenessClientCallback mCallback;
59         private boolean mGraphIsRunning;
60 
MockOasHal()61         MockOasHal() {}
62 
63         /** Returns whether the mock graph is running. */
isGraphRunning()64         public boolean isGraphRunning() {
65             return mGraphIsRunning;
66         }
67 
68         @Override
getLatestDetection(OccupantDetections detections)69         public void getLatestDetection(OccupantDetections detections) {}
70 
71         @Override
setCallback(IOccupantAwarenessClientCallback callback)72         public void setCallback(IOccupantAwarenessClientCallback callback) {
73             mCallback = callback;
74         }
75 
76         @Override
getState(int occupantRole, int detectionCapability)77         public @OccupantAwarenessStatus byte getState(int occupantRole, int detectionCapability) {
78             return OccupantAwarenessStatus.READY;
79         }
80 
81         @Override
startDetection()82         public @OccupantAwarenessStatus byte startDetection() {
83             mGraphIsRunning = true;
84             return OccupantAwarenessStatus.READY;
85         }
86 
87         @Override
stopDetection()88         public @OccupantAwarenessStatus byte stopDetection() {
89             mGraphIsRunning = false;
90             return OccupantAwarenessStatus.READY;
91         }
92 
93         @Override
getCapabilityForRole(@ole int occupantRole)94         public int getCapabilityForRole(@Role int occupantRole) {
95             if (occupantRole == OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER) {
96                 return SystemStatusEvent.DETECTION_TYPE_PRESENCE
97                         | SystemStatusEvent.DETECTION_TYPE_GAZE
98                         | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING;
99             } else if (occupantRole
100                     == OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER) {
101                 return SystemStatusEvent.DETECTION_TYPE_PRESENCE;
102             } else {
103                 return SystemStatusEvent.DETECTION_TYPE_NONE;
104             }
105         }
106 
107         /** Causes a status event to be generated with the specified flags. */
fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status)108         public void fireStatusEvent(int detectionFlags, @OccupantAwarenessStatus byte status)
109                 throws RemoteException {
110             if (mCallback != null) {
111                 mCallback.onSystemStatusChanged(detectionFlags, status);
112             }
113         }
114 
115         /** Causes a status event to be generated with the specified detection event data. */
fireDetectionEvent(OccupantAwarenessDetection detectionEvent)116         public void fireDetectionEvent(OccupantAwarenessDetection detectionEvent)
117                 throws RemoteException {
118             if (mCallback != null) {
119                 OccupantDetection detection = new OccupantDetection();
120 
121                 OccupantDetections detections = new OccupantDetections();
122                 detections.timeStampMillis = TIMESTAMP;
123                 detections.detections = new OccupantDetection[] {detection};
124                 mCallback.onDetectionEvent(detections);
125             }
126         }
127 
128         @Override
getInterfaceVersion()129         public int getInterfaceVersion() {
130             return this.VERSION;
131         }
132 
133         @Override
getInterfaceHash()134         public String getInterfaceHash() {
135             return this.HASH;
136         }
137     }
138 
139     private MockOasHal mMockHal;
140     private com.android.car.OccupantAwarenessService mOasService;
141 
142     private CompletableFuture<SystemStatusEvent> mFutureStatus;
143     private CompletableFuture<OccupantAwarenessDetection> mFutureDetection;
144 
145     @Before
setUp()146     public void setUp() {
147         Context context = ApplicationProvider.getApplicationContext();
148         mMockHal = new MockOasHal();
149         mOasService = new com.android.car.OccupantAwarenessService(context, mMockHal);
150         mOasService.init();
151 
152         resetFutures();
153     }
154 
155     @After
tearDown()156     public void tearDown() {
157         mOasService.release();
158     }
159 
160     @Test
testWithNoRegisteredListeners()161     public void testWithNoRegisteredListeners() throws Exception {
162         // Verify operation when no listeners are registered.
163         mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY);
164 
165         // Nothing should have been received.
166         assertThat(mFutureStatus.isDone()).isFalse();
167         assertThat(mFutureDetection.isDone()).isFalse();
168     }
169 
170     @Test
testStatusEventsWithRegisteredListeners()171     public void testStatusEventsWithRegisteredListeners() throws Exception {
172         // Verify correct operation when a listener has been registered.
173         registerCallbackToService();
174         SystemStatusEvent result;
175 
176         // Fire a status event and ensure it is received.
177         // "Presence status is ready"
178         resetFutures();
179         mMockHal.fireStatusEvent(
180                 IOccupantAwareness.CAP_PRESENCE_DETECTION, OccupantAwarenessStatus.READY);
181 
182         result = mFutureStatus.get(1, TimeUnit.SECONDS);
183         assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_PRESENCE);
184         assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_READY);
185 
186         // "Gaze status is failed"
187         resetFutures();
188         mMockHal.fireStatusEvent(
189                 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.FAILURE);
190 
191         result = mFutureStatus.get(1, TimeUnit.SECONDS);
192         assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_GAZE);
193         assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_SYSTEM_FAILURE);
194 
195         // "Driver monitoring status is not-ready"
196         resetFutures();
197         mMockHal.fireStatusEvent(
198                 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION,
199                 OccupantAwarenessStatus.NOT_INITIALIZED);
200 
201         result = mFutureStatus.get(1, TimeUnit.SECONDS);
202         assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING);
203         assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_NOT_READY);
204 
205         // "None is non-supported"
206         resetFutures();
207         mMockHal.fireStatusEvent(
208                 IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.NOT_SUPPORTED);
209 
210         result = mFutureStatus.get(1, TimeUnit.SECONDS);
211         assertEquals(result.detectionType, SystemStatusEvent.DETECTION_TYPE_NONE);
212         assertEquals(result.systemStatus, SystemStatusEvent.SYSTEM_STATUS_NOT_SUPPORTED);
213     }
214 
215     @Test
test_unregisteredListeners()216     public void test_unregisteredListeners() throws Exception {
217         // Verify that listeners are successfully unregistered.
218         IOccupantAwarenessEventCallback callback = registerCallbackToService();
219 
220         // Unregister the registered listener.
221         mOasService.unregisterEventListener(callback);
222 
223         // Fire some events.
224         mMockHal.fireStatusEvent(IOccupantAwareness.CAP_NONE, OccupantAwarenessStatus.READY);
225         mMockHal.fireStatusEvent(
226                 IOccupantAwareness.CAP_GAZE_DETECTION, OccupantAwarenessStatus.READY);
227         mMockHal.fireStatusEvent(
228                 IOccupantAwareness.CAP_DRIVER_MONITORING_DETECTION, OccupantAwarenessStatus.READY);
229 
230         // Nothing should have been received.
231         assertThat(mFutureStatus.isDone()).isFalse();
232         assertThat(mFutureDetection.isDone()).isFalse();
233 
234         // Unregister a second time should log an error, but otherwise not cause any action.
235         mOasService.unregisterEventListener(callback);
236     }
237 
238     @Test
test_getCapabilityForRole()239     public void test_getCapabilityForRole() throws Exception {
240         assertThat(
241                         mOasService.getCapabilityForRole(
242                                 OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER))
243                 .isEqualTo(
244                         SystemStatusEvent.DETECTION_TYPE_PRESENCE
245                                 | SystemStatusEvent.DETECTION_TYPE_GAZE
246                                 | SystemStatusEvent.DETECTION_TYPE_DRIVER_MONITORING);
247 
248         assertThat(
249                         mOasService.getCapabilityForRole(
250                                 OccupantAwarenessDetection.VEHICLE_OCCUPANT_FRONT_PASSENGER))
251                 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_PRESENCE);
252 
253         assertThat(
254                         mOasService.getCapabilityForRole(
255                                 OccupantAwarenessDetection.VEHICLE_OCCUPANT_ROW_2_PASSENGER_RIGHT))
256                 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE);
257 
258         assertThat(
259                         mOasService.getCapabilityForRole(
260                                 OccupantAwarenessDetection.VEHICLE_OCCUPANT_NONE))
261                 .isEqualTo(SystemStatusEvent.DETECTION_TYPE_NONE);
262     }
263 
264     @Test
test_serviceStartsAndStopGraphWithListeners()265     public void test_serviceStartsAndStopGraphWithListeners() throws Exception {
266         // Verify that the service starts the detection graph when the first client connects, and
267         // stop when the last client disconnects.
268 
269         // Should be not running on start (no clients are yet connected).
270         assertThat(mMockHal.isGraphRunning()).isFalse();
271 
272         // Connect a client. Graph should be running.
273         IOccupantAwarenessEventCallback first_client = registerCallbackToService();
274         assertThat(mMockHal.isGraphRunning()).isTrue();
275 
276         // Connect a second client. Graph should continue running.
277         IOccupantAwarenessEventCallback second_client = registerCallbackToService();
278         assertThat(mMockHal.isGraphRunning()).isTrue();
279 
280         // Remove the first client. Graph should continue to run since a client still remains.
281         mOasService.unregisterEventListener(first_client);
282         assertThat(mMockHal.isGraphRunning()).isTrue();
283 
284         // Remove the second client. Graph should now stop since all clients have now closed.
285         mOasService.unregisterEventListener(second_client);
286         assertThat(mMockHal.isGraphRunning()).isFalse();
287     }
288 
289     /** Registers a listener to the service. */
registerCallbackToService()290     private IOccupantAwarenessEventCallback registerCallbackToService() {
291         IOccupantAwarenessEventCallback callback =
292                 new IOccupantAwarenessEventCallback.Stub() {
293                     @Override
294                     public void onStatusChanged(SystemStatusEvent systemStatusEvent) {
295                         mFutureStatus.complete(systemStatusEvent);
296                     }
297 
298                     public void onDetectionEvent(OccupantAwarenessDetection detectionEvent) {
299                         mFutureDetection.complete(detectionEvent);
300                     }
301                 };
302 
303         mOasService.registerEventListener(callback);
304         return callback;
305     }
306 
307     /** Resets futures for testing. */
resetFutures()308     private void resetFutures() {
309         mFutureStatus = new CompletableFuture<>();
310         mFutureDetection = new CompletableFuture<>();
311     }
312 }
313